When multithreading is enabled (it can be switched on and off in the
preferences dialog or the Edit menu), you can create diagrams that model
arbitarily many sequences running in parallel, not just a single one. The
sequences can (at your option) be distinguished by the colours of their
corresponding lifelines. There is still no real parallelism, as the
messages must be specified in some order. This order can be interpreted as
one of many possible interleavings of the threads. A
single-processor system would execute them in a similar way.
Specifying a multithreaded diagram is only slightly harder than
specifying a diagram with a single thread.
A newly spawned thread can be distinguished from older threads by
the colour of its corresponding lifelines. If you spawn too many
threads, colours may be repeated. Threads have successive numbers,
starting with 0.
Objects that have been declared with the "t"
flag set, for example:
o:Object[t]
have their own (statically spawned) thread. The first thing that
happens on this thread must be a message sent by the corresponding
object.
If there is no object with a "t" flag, there is
a single thread nonetheless. It starts executing when the first
message is sent by an object.
As a rule, a message sent by an actor to an object (dynamically)
spawns a new thread.
So do messages sent to "active objects". An
object can be declared to be active by setting the v
flag. Example:
worker:BackgroundWorker[v]
Messages sent and received by ordinary objects may also spawn new
threads. In order to specify that such a message is spawning, use a
colon followed by a ">" to separate the
caller from the message. Example:
object:>bar.foo()
This means that object spawns a new thread and the
first thing this new thread does is executing the foo
method of the bar object.
Note that you cannot specify answers to messages that spawn
threads. If an object running on a new thread wants to communicate
with the spawning object, it must explicitly send a message.
A message can have more than one callee, specified in set
notation. In this case, it is a broadcast message. Example:
caller:{callee1,callee2,callee3}.broadcast
A callee may not occur more than once in the callee set, the
caller may not occur at all in there. The effect of a broadcast
message is that a new thread is spawned for each callee.
It is currently not possible to define mnemonics for objects that
are activated by a broadcast message.
When more than one thread is used, we need to be able to tell on
which of the threads a message occurs. So the level of a caller
(specified in square brackets) may be followed by the number of the
thread, where level and thread are separated by a comma. Example:
object[0,3]:bar.foo()
This means that object is sending a foo()
message to bar on the thread number 3.
If an object is used by a single thread only, the number of the
thread can be omitted. If there is more than one thread using an object
and the thread number is omitted, the editor assumes that the message is
to be sent on the same thread as the most recent message. If you spawn a
new thread, the next message will, if not otherwise specified, be sent
on that new thread. It is also possible to omit the level and only
specify the thread, so object[,3]:bar.foo() is equivalent
to the example above, as 0 is the default level.
You can also use a mnemonic between square brackets. When a
mnemonic is defined for an object, the mnemonic determines both the
level and the thread number.
Thread numbers can be made visible at your option (see the
"Threads" menu): They are shown at the top of the
active lifelines.
Normally, the focus of a thread's activity stays on the object that
has received the most recent message, unless the thread is stopped or
another message is sent. Sometimes, however, it may seem inappropriate
to have a thread's focus resting on an object for so long. In that case
a message should be ended by an "&". This
means that the answer to the message will be sent immediately. Note that
a message sent later cannot have the callee of the instantly returning
message as a caller of course. Example:
foo:bar.notify()&
If a spawning message is suffixed with an
"&", the spawned thread does nothing and dies
instantly. Such messages might be interpreted as asynchronous signals.
Normally, a thread runs until the end of the diagram. But maybe you
would like to model that it dies at some point. This is done by
inserting a pseudo message of the form:
object:stop
(where of course also a thread number may be specified). Stopping a
thread may be necessary before an object can be destroyed, if the
thread's activity does not return from the object otherwise. The text of
the pseudo message (i. e. "stop") will not appear on
the diagram. The visible effect rather is that the corresponding
lifeline ends at that point.
Before an object sends a message, a thread's control flow has to
return to it, which implies that some answers pending on the thread
might have to be sent. Sometimes one wants to manipulate the control
flow like this without the need to send a new message, e. g. for
representing that a certain set of actions is finished now or for simply
doing some "clean-up". This can be done by inserting a
pseudo message consisting of just an underscore:
object:_
The effect is that enough pending answers will be sent such that
"object" could send a message now on its
current thread (again, a special thread number may be specified). The
pseudo message does not appear on the diagram and there will be no extra
space consumed (apart from the space needed for the answers).