Content deleted Content added
Line 72:
Now, consider the case of two timelines (two threads). One thread, call it thread A, executes some statements, call them A-pre-gain-lock statements. Then thread A executes "gain ownership of the lock", then thread A executes A-post-gain-lock statements, which come after A gains ownership of the lock. Finally, thread A performs "give up ownership of the lock". Then thread A performs A-post-giveup-lock statements.
A second thread, call it thread
"In the case that ownership of the lock goes from thread A to thread B, A-post-gain-lock statements come before B-post-gain-lock statements."
Line 80:
And that's it.
Seems simple, right? The complication comes from the fact that the execution model does not have any means for the execution of "give up ownership of the lock" to have any influence over which execution of "gain ownership of the lock" in some other timeline (thread) follows. Very often, only certain handoffs give valid results. Thus, the programmer must think of all possible combinations of one thread giving up a lock and another thread getting it next, and make sure their code only allows valid combinations. (Note also that the only effect is that A-post-gain-lock statements come before B-post-gain-lock statements. No other effect happens, and no other relative ordering can be relied upon. Specifically, B-post-gain-lock and A-post-give-up-lock have ''no relative ordering'' defined, which surprises many people. But thread A may have been swapped out after giving up ownership, so A-post-give-up-lock statements may happen long after many B-post-gain-lock statements have finished. That is one of the possibilities that must be thought about when designing locks, and illustrates why multi-threaded programming is difficult.
Note that modern parallel languages have much easier to use execution models. The thread model was one of the original parallel execution models, which may account for why it has persisted despite having a difficult to use execution model.
== See also ==
|