...
In this example, each thread has its own distinct condition predicate because each thread requires current_step
to have a different value before proceeding. Upon the signal operation (pthreadcnd_cond_signal()
), any of the waiting threads can wake up. If, by chance, it the notified thread is not the thread with the next step value, that thread will wait again (pthreadcnd_cond_wait()
), resulting in a deadlock situation because no more signal operations will notifications can occur.
Consider the following example:
...
This noncompliant code example violates the liveness property.
Compliant Solution (
...
cnd_broadcast()
)
This compliant solution uses the cnd_broadcast()
method function to signal all waiting threads instead of a single random onethread. Only the run_step()
thread code from the noncompliant code example is modified, as follows:
...
The fact that all threads will be awake solves the problem because each one ends up executing its condition predicate test; , and exactly one will find its test to be true succeed and will continue the execution until the end.
Compliant Solution (Windows, Condition Variables)
This compliant solution uses a CONDITION_VARIABLE
object, available on Microsoft Windows (Vista and later).
Code Block | ||||
---|---|---|---|---|
| ||||
#include <Windows.h> #include <stdio.h> CRITICAL_SECTION lock; CONDITION_VARIABLE cond; DWORD WINAPI run_step(LPVOID t) { static int current_step = 0; int my_step = (int)t; EnterCriticalSection(&lock); printf("Thread %d has the lock\n", my_step); while (current_step != my_step) { printf("Thread %d is sleeping...\n", my_step); if (!SleepConditionVariableCS(&cond, &lock, INFINITE)) { /* Handle error condition. */ } printf("Thread %d woke up\n", my_step); } /* Do processing... */ printf("Thread %d is processing...\n", my_step); current_step++; LeaveCriticalSection(&lock); /* Signal ALL waiting tasks. */ WakeAllConditionVariable(&cond); printf("Thread %d is exiting...\n", my_step); return 0; } enum { NTHREADS = 5 }; int main(int argc, char** argv) { HANDLE threads[NTHREADS]; InitializeCriticalSection(&lock); InitializeConditionVariable(&cond); /* Create threads. */ for (int i = 0; i < NTHREADS; ++i) { threads[i] = CreateThread(NULL, 0, run_step, (LPVOID)i, 0, NULL); } /* Wait for all threads to complete. */ WaitForMultipleObjects(NTHREADS, threads, TRUE, INFINITE); DeleteCriticalSection(&lock); return 0; } |
...