...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <threads.h> enum { NTHREADS = 5 }; mtx_t mutex; cnd_t cond; int run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = mtx_lock(&mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d has the lock\n", my_step); while (current_step != my_step) { printf("Thread %d is sleeping...\n", my_step); if ((result = cnd_wait(&cond, &mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d woke up\n", my_step); } /* Do processing... */ printf("Thread %d is processing...\n", my_step); current_step++; /* Signal a waiting task */ if ((result = cnd_signal(&cond)) != thrd_success) { /* Handle error condition */ } printf("Thread %d is exiting...\n", my_step); if ((result = mtx_unlock(&mutex)) != thrd_success) { /* Handle error condition */ } thrd_exit(0); return 0; } int main(int argc, char** argv) { int i; int result; thrd_t threads[NTHREADS]; int step[NTHREADS]; if ((result = mtx_init(&mutex, mtx_plain)) != thrd_success) { /* Handle error condition */ } if ((result = cnd_init(&cond)) != thrd_success) { /* Handle error condition */ } /* Create threads */ for (i = 0; i < NTHREADS; i++) { step[i] = i; if ((result = thrd_create(&threads[i], run_step, (void *)step[i])) != thrd_success) { /* Handle error condition */ } } /* Wait for all threads to complete */ for (i = NTHREADS-1; i >= 0; i--) { if ((result = thrd_join(threads[i], NULL)) != thrd_success) { /* Handle error condition */ } } mtx_destroy(&mutex); cnd_destroy(&cond); return thrd_exit(0); } |
In this example, each thread has its own predicate because each requires current_step
to have a different value before proceeding. Upon the signal operation (pthread_cond_signal()
), any of the waiting threads can wake up. If, by chance, it is not the thread with the next step value, that thread will wait again (pthread_cond_wait()
), resulting in a deadlock situation because no more signal operations will occur.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <threads.h> #include <stdio.h> int run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = mtx_lock(&mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d has the lock\n", my_step); while (current_step != my_step) { printf("Thread %d is sleeping...\n", my_step); if ((result = cnd_wait(&cond, &mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d woke up\n", my_step); } /* Do processing... */ printf("Thread %d is processing...\n", my_step); current_step++; /* Signal ALL waiting tasks */ if ((result = cnd_broadcast(&cond)) != thrd_success) { /* Handle error condition */ } printf("Thread %d is exiting...\n", my_step); if ((result = mtx_unlock(&mutex)) != 0) { /* Handle error condition */ } thrd_exit(0); return 0; } |
The fact that all threads will be awake solves the problem because each one ends up executing its predicate test; one will find its test to be true and will continue the execution until the end.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <threads.h> enum { NTHREADS = 5 }; mtx_t mutex; cnd_t cond[NTHREADS]; int run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = mtx_lock(&mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d has the lock\n", my_step); while (current_step != my_step) { printf("Thread %d is sleeping...\n", my_step); if ((result = cnd_wait(&cond[my_step], &mutex)) != thrd_success) { /* Handle error condition */ } printf("Thread %d woke up\n", my_step); } /* Do processing... */ printf("Thread %d is processing...\n", my_step); current_step++; /* Signal next step thread */ if ((my_step + 1) < NTHREADS) { if ((result = cnd_signal(&cond[my_step+1])) != thrd_success) { /* Handle error condition */ } } printf("Thread %d is exiting...\n", my_step); if ((result = mtx_unlock(&mutex)) != thrd_success) { /* Handle error condition */ } thrd_exit(0); return 0; } int main(int argc, char** argv) { int i; int result; thrd_t threads[NTHREADS]; int step[NTHREADS]; if ((result = mtx_init(&mutex, mtx_plain)) != thrd_success) { /* Handle error condition */ } for (i = 0; i< NTHREADS; i++) { if ((result = cnd_init(&cond[i])) != thrd_success) { /* Handle error condition */ } } /* Create threads */ for (i = 0; i < NTHREADS; i++) { step[i] = i; if ((result = thrd_create(&threads[i], run_step, (void *)step[i])) != thrd_success) { /* Handle error condition */ } } /* Wait for all threads to complete */ for (i = NTHREADS-1; i >= 0; i--) { if ((result = thrd_join(threads[i], NULL)) != thrd_success) { /* Handle error condition */ } } mtx_destroy(&mutex); for (i = 0; i < NTHREADS; i++) { cnd_destroy(&cond[i]); } return thrd_exit(0); } |
In this compliant code, each thread has associated a unique condition variable that is signaled when that particular thread needs to be awakened. This solution turns out to be more efficient because only the desired thread is awakened.
...