...
The following noncompliant code example consists on a given number of threads (5) that should execute one after another according to the step level that is assigned to them when created (serialized processing). The current_step
variable holds the current step level and is incremented as soon as the respective thread finishes its processing. Finally, another thread is signaled so that the next step can be executed.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #define NTHREADS 5 pthread_mutex_t mutex; pthread_cond_t cond; void *run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = pthread_mutex_lock(&mutex)) != 0) { /* 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 = pthread_cond_wait(&cond, &mutex)) != 0) { /* 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 = pthread_cond_signal(&cond)) != 0) { /* Handle error condition */ } printf("Thread %d is exiting...\n", my_step); if ((result = pthread_mutex_unlock(&mutex)) != 0) { /* Handle error condition */ } pthread_exit(NULL); } int main(int argc, char** argv) { int i; int result; pthread_attr_t attr; pthread_t threads[NTHREADS]; int step[NTHREADS]; if ((result = pthread_mutex_init(&mutex, NULL)) != 0) { /* Handle error condition */ } if ((result = pthread_cond_init(&cond, NULL)) != 0) { /* Handle error condition */ } if ((result = pthread_attr_init(&attr)) != 0) { /* Handle error condition */ } if ((result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) != 0) { /* Handle error condition */ } /* Create threads */ for (i = 0; i < NTHREADS; i++) { step[i] = i; if ((result = pthread_create(&threads[i], &attr, run_step, (void *)step[i])) != 0) { /* Handle error condition */ } } /* Wait for all threads to complete */ for (i = NTHREADS-1; i >= 0; i--) { if ((result = pthread_join(threads[i], NULL)) != 0) { /* Handle error condition */ } } if ((result = pthread_mutex_destroy(&mutex)) != 0) { /* Handle error condition */ } if ((result = pthread_cond_destroy(&cond)) != 0) { /* Handle error condition */ } if ((result = pthread_attr_destroy(&attr)) != 0) { /* Handle error condition */ } pthread_exit(NULL); } |
...
This compliant solution uses the pthread_cond_broadcast()
method to signal all waiting threads instead of a single "random" one.
Only the run_step()
thread code from the noncompliant code example is modified, as follows:
Code Block | ||||
---|---|---|---|---|
| ||||
void *run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = pthread_mutex_lock(&mutex)) != 0) { /* 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 = pthread_cond_wait(&cond, &mutex)) != 0) { /* 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 = pthread_cond_broadcast(&cond)) != 0) { /* Handle error condition */ } printf("Thread %d is exiting...\n", my_step); if ((result = pthread_mutex_unlock(&mutex)) != 0) { /* Handle error condition */ } pthread_exit(NULL); } |
...
Another way to solve the signal issue is to use a unique condition variable for each thread (maintaining a single mutex associated with it). In this case, the signal operation (pthread_cond_signal()
) will wake up the only thread waiting on it.
NOTE: The predicate of the signaled thread must be true; otherwise, a deadlock can occur anyway.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #define NTHREADS 5 pthread_mutex_t mutex; pthread_cond_t cond[NTHREADS]; void *run_step(void *t) { static int current_step = 0; int my_step = (int)t; int result; if ((result = pthread_mutex_lock(&mutex)) != 0) { /* 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 = pthread_cond_wait(&cond[my_step], &mutex)) != 0) { /* 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 = pthread_cond_signal(&cond[my_step+1])) != 0) { /* Handle error condition */ } } printf("Thread %d is exiting...\n", my_step); if ((result = pthread_mutex_unlock(&mutex)) != 0) { /* Handle error condition */ } pthread_exit(NULL); } int main(int argc, char** argv) { int i; int result; pthread_attr_t attr; pthread_t threads[NTHREADS]; int step[NTHREADS]; if ((result = pthread_mutex_init(&mutex, NULL)) != 0) { /* Handle error condition */ } for (i = 0; i< NTHREADS; i++) { if ((result = pthread_cond_init(&cond[i], NULL)) != 0) { /* Handle error condition */ } } if ((result = pthread_attr_init(&attr)) != 0) { /* Handle error condition */ } if ((result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) != 0) { /* Handle error condition */ } /* Create threads */ for (i = 0; i < NTHREADS; i++) { step[i] = i; if ((result = pthread_create(&threads[i], &attr, run_step, (void *)step[i])) != 0) { /* Handle error condition */ } } /* Wait for all threads to complete */ for (i = NTHREADS-1; i >= 0; i--) { if ((result = pthread_join(threads[i], NULL)) != 0) { /* Handle error condition */ } } if ((result = pthread_mutex_destroy(&mutex)) != 0) { /* Handle error condition */ } for (i = 0; i < NTHREADS; i++) { if ((result = pthread_cond_destroy(&cond[i])) != 0) { /* Handle error condition */ } } if ((result = pthread_attr_destroy(&attr)) != 0) { /* Handle error condition */ } pthread_exit(NULL); } |
...