Conquering Concurrency


Normally when you construct communication diagrams, the messages are all sequential—you can use a traditional, outline-style numbering scheme to indicate the order of the messages, and only one message is ever active at a time. Of course, in sophisticated multithreaded systems, you may have multiple threads running at once. If you refer to Figure 14-7, for example, you can see that the Bill object, when it has to return information to the GenerateBill object, does so with a call back to the GenerateBill rather than with a traditional return. We designed it this way because the Bill has lots of work to do that doesn’t involve the GenerateBill. If we can free up the GenerateBill controller, it may be able to work with other guests to generate other bills while our Bill is busy. We treat Bill an active object that has its own thread of control distinct from that of the use case so the two objects can run independently.

Whenever you have a class or object that owns its own thread of control that it is able to run independently of its caller, you have an active object. You might want to use an optional notation on the Bill object to indicate that it is an active object. You indicate this by placing parallel, vertical bars next to the left and right sides of the class or object box (as in Figure 14-8).


Figure 14-8: A communication diagram showing concurrency.

 Technical Stuff   We’ve been throwing the words concurrency and concurrently around a bit—and yes, you could run to the nearest dictionary and come up with a definition for them. Here, however, concurrency has a formal meaning in computer science and the world of UML—one that differs slightly from its everyday meaning.

If two events, A and B, are concurrent, the following must be true:

  • There is no causal relationship between A an B (neither causes the other).

  • A can occur before B, or B can occur before A.

  • A and B can occur simultaneously (it’s not logically impossible).

A and B don’t have to run simultaneously—in fact, that’s pretty rare, and it often has more to do with how precisely you record your time. And in single processors, A and B can’t really run simultaneously, unless they swap in and out in a time-sharing way.

Looping concurrently

Whenever you indicate a loop in UML, the normal interpretation is that each iteration of the loop runs sequentially: The first iteration runs and finishes, and then next iteration runs and finishes, and so on until the last iteration. Often, however, this interpretation is overly restrictive—and not strictly necessary. If the results of the loop would be the same, no matter what the order of iteration (say, counting down instead of up), then you may be able to make all the iterations run concurrently. On some hardware, the compiler automatically detects whether the results of an iteration depend on the order—if they don’t, the compiler forwards each iteration to a parallel processor.

If you want to have the loop iterations run independently and concurrently, use the following syntax:

 3.4*||(loopIndex=lowValue..HighValue): msg() 

Adding the two bars indicates that you want the iterations of the loop done concurrently (or in parallel—in which case, the bars are parallel ). Adding the bars doesn’t guarantee that the implementation will be done that way—after all, it’s sometimes a platform consideration. For example, some platforms can’t do parallel loops at all, and some can do no more than 255 at a time. But adding the bars does signal your intent that no loop iteration depend on any other—and that you prefer a parallel implementation. You can see an example of this concurrent looping in Figure 14-8 if you look at the following message:

 3.4b.2*||[thisCharge=1..numCharges]:
rc[thisCharge] = getRoomCharge()

The getRoomCharge() is a simple retrieval operation, so all the charges are retrieved at once (concurrently) and stored into a local array called rc[]. We show the assignment to rc[thisCharge] because we are using the lifeline/part notation and [thisCharge] is the selector (or qualifier) that indicates which object we are setting. (Ignore the b in the message number; it indicates a thread, which we explain later in this chapter.)

Identifying independent threads

If you work with multi-threaded systems, you may want to be explicit about concurrent processes. In UML, if you want to indicate that messages are to be sent concurrently, you have to give them the same sequence number. But to distinguish them, you give them individual names. For example, the following three messages would be sent concurrently, as they all share the same sequence number 4.1.

 4.1cotton:       msg1() 4.1nylon:        msg2() 4.1polyester:    msg3() 

All three threads run concurrently. Each of the different threads has a character string tag that can be used to identify it (cotton, nylon, or polyester). If you don’t sew, you can use thread names like a, b, or c.

The thread names are useful because you still want to be able to identify subordinate messages on a communication diagram. For example, the following message executions have to obey the rules that govern subordinate sequence numbers:

 4.1cotton:  4.1cotton.1: 4.1cotton.2: 4.1cotton.2.1: 4.1nylon: 4.1nylon.1: 4.1nylon.1.1: 4.1nylon.1.2: 4.1polyester: 

These rules require that 4.1cotton.1: finishes before 4.1cotton.2: can start. And before 4.1cotton.2: can finish, 4.1cotton.2.1: must finish. To have 4.1cotton:: finish, 4.1cotton.2: must finish also.

A similar ordering occurs with the nylon thread. However, because the two threads are concurrent, you can’t say anything about the relative order of any cotton message or nylon message. You could have 4.1nylon.1.2: running before 4.1cotton.1: finishes or vice versa.

In Figure 14-8, there are two independent threads:

 3.4a*[thisDay=sd..ed]: getLodgingCharges(thisDay) 3.4b*[thisDay=sd..ed]: getRoomCharges(thisDay) 

In each thread, there is a loop over the number of days in the stay. Because each loop is a normal loop, each iteration of each loop occurs in order. But because the two loops are concurrent, the two loops are not in synch and could finish in any order.

In both threads, there are subordinate steps. In the 3.4a thread, for each iteration of the loop there is a call to the Lodging and the Lodging then calls the RoomRate. In the 3.4b thread, concurrent with the 3.4a thread, each iteration of the loop has the Stay asking itself for the number of charges and then in a parallel loop, asking the RoomCharges for their values.




UML 2 for Dummies
UML 2 For Dummies
ISBN: 0764526146
EAN: 2147483647
Year: 2006
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net