The Condition Variable Model and Safety PropertiesThreaded programs are much easier to develop, understand, and maintain if we use well-understood and familiar techniques and models. Chapter 7 discussed this and introduced the boss/worker and work crew models to establish a useful framework for understanding many threaded programs. The critical section concept is essential when using mutexes, and describing the invariants of your data structure is very useful. Finally, even defects have models, as we saw with the deadlock example. Note: Microsoft has its own set of models, such as the apartment model and free threading. These terms are most often used with COM and are discussed briefly at the end of Chapter 11. Using Events and Mutexes TogetherThe next step is to describe how to use mutexes and events together, generalizing Program 8-2, where we had the following situation, which will occur over and over again. Note: This discussion applies to CRITICAL_SECTIONs as well as to mutexes.
The Condition Variable ModelNow let's combine all of this into a single code fragment that represents what we will call the condition variable model (CV model) with two variations, the signal and broadcast CV models. The first examples use the broadcast variation. The result is a program model that will occur many times and can be used to solve a wide variety of synchronization problems. For convenience, the example is stated in terms of a producer and a consumer. The discussion may seem a bit abstract, but once the techniques are understood, we will be able to solve a number of synchronization problems that would be very difficult without a good model. The code fragment has several key elements.
The following code segment shows a producer and consumer using these principles, with a single event and condition variable predicate (implemented with a function, cvp, that is assumed but not shown). When the producer signals that a desired state has been reached, this example assumes that it is appropriate to release several consumer threadsthat is, the signal should be broadcast to all waiting consumers. For instance, the producer may have created several messages, and the state is changed by increasing the message count. In many situations, you want to release only a single thread, as will be discussed after the code fragment. This code segment is designed to operate under Windows 9x as well as all NT versions. SignalObjectAndWait will then be used to simplify the solution. Note and caution:
Comments on the Condition Variable ModelThe essential feature in the code segment is the loop in the consumer. The loop body consists of three steps: (1) unlock the mutex that was locked prior to entering the loop, (2) wait on the event, and (3) lock the mutex again. The event wait time-out is significant, as explained later. Pthreads, as implemented in many UNIX and other systems, combine these three steps into a single function, pthread_cond_wait, combining a mutex and a condition variable (which is similar but not identical to the Windows event). This is the reason for using the term condition variable model. There is also a timed version, which allows a time-out on the event wait. Importantly, the single Pthreads function implements the first two steps (the mutex release and event wait) as an atomic operation so that no other thread can run before the calling thread waits on the event (or condition variable). The Pthreads designers made a wise choice; the two functions (with and without a time-out) are the only ways to wait on a condition variable in Pthreads, so a condition variable must always be used with a mutex. Windows forces you to use two or three separate function calls, and you need to do it in just the right way to avoid problems. Another motivation for learning the CV model, besides the fact that it simplifies programs and is essential if you ever need to use Pthreads, is that several third parties implement OS-independent thread and synchronization classes based on the CV model. With the material here, you will be able to understand those implementations quickly. Note: Using the Condition Variable ModelThe condition variable model, when implemented properly, works as follows.
Condition Variable Model VariationsNotice, first, that the preceding code fragment uses a manual-reset event and calls PulseEvent rather than SetEvent. Is this the correct choice, and could the event be used differently? The answer is "yes" to both questions. Referring back to Table 8-1, we see that the example has the property that multiple threads will be released. This is correct in this example, where several messages are produced and there are multiple consuming threads, and we need to broadcast the change. However, if the producer creates just one message and there are multiple consuming threads, the event should be auto-reset and the producer should call SetEvent to ensure that exactly one thread is released. This variation is the signal CV model rather than the broadcast CV model. It is still essential for the released consumer thread, which will then own the mutex, to modify the object to indicate that there is no available message (that is, the condition variable predicate will no longer hold). Of the four combinations in Table 8-1, two are useful in the condition variable model. Considering the other two combinations, auto-reset/PulseEvent would have the same effect as auto-reset/SetEvent (the signal CV model) because of the time-out, but the dependence on the time-out would reduce responsiveness. The manual-reset/SetEvent combination causes spurious signals (the condition variable predicate test offers protection, however), because some thread must reset the event, and there will be a race among the threads before the event is reset. In summary, auto-reset/SetEvent is the signal CV model, which releases a single waiting thread, while manual-reset/PulseEvent is the broadcast CV model, which releases all waiting threads. Pthreads has the same distinction but does not require the finite time-out in the event wait for the broadcast model, whereas the time-out is essential in Windows because the mutex release and event wait are not performed atomically. This will change, however, when we introduce SignalObjectAndWait. An Example Condition Variable PredicateConsider the condition variable predicate:
In this case, a consumer thread will wait until the count is sufficiently large, and the producer can increment the count by an arbitrary amount. This shows, for example, how to implement a multiple-wait semaphore; recall that normal semaphores do not have an atomic wait for multiple units. The consumer thread would then decrement the count by K after leaving the loop but before releasing the mutex. Notice that the broadcast CV model is appropriate in this case because a single producer may increase the count so as to satisfy several but not all of the waiting consumers. Semaphores and the Condition Variable ModelIn some cases, a semaphore would be appropriate rather than an event, and semaphores have the advantage of specifying the exact number of threads to be released. For example, if each consumer were known to consume exactly one message, the producer could call ReleaseSemaphore with the exact number of messages produced. In the more general case, however, the producer does not know how the individual consumers will modify the state variable structure, so the condition variable model can be used to solve a wider class of problems. The CV model is powerful enough to implement semaphores. As described earlier, the basic technique is to define a predicate stating that "the semaphore count is nonzero" and create a state structure containing the count and maximum value. Exercise 1011 shows a complete solution that allows for an atomic wait for multiple units. Pthreads do not provide semaphores because condition variables are sufficiently powerful. |
Saturday, December 19, 2009
The Condition Variable Model and Safety Properties
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment