The main limitation with locks (mutex) is that applications have to wait for another block of code to end
its execution to continue. That eventually leads, for practical purposes, a thread having to wait for a signal from another have,
to wait through active loops rechecking its status to continue. Offering a safe and eficient locking mechanism, the pthread_cond
appears.
This condition variable has two associated functions that allow it to decide to wait locked (with pthread_cond_wait
),
which will be signaled from another with different thread with pthread_cond_signal
.
As in the previous case of the mutex variable codición you can be configured.
The following snippet shows a program where two threads have to wait for another end to continue their execution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | // compile with $ gcc -Wall -g *.c -pthread -o program // run with ./program // check with valgrind --tool=helgrind ./program #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex; void* thread_run(void* data_exit) { int i_tmp=-1; sleep(2); printf("[TH_ID:%ld]: Hello from the thread \n", pthread_self()); printf("[TH_ID:%ld]: Reading %i \n", pthread_self(),(*(int*)data_exit)); i_tmp=(*(int*)data_exit); for(;i_tmp==0;) { pthread_mutex_lock(&mutex); i_tmp=(*(int*)data_exit); if(i_tmp!=0) { (*(int*)data_exit)--; pthread_mutex_unlock(&mutex); return data_exit; } else { pthread_mutex_unlock(&mutex); printf("[TH_ID:%ld]: sleeping 2 seconds, %i \n", pthread_self(),i_tmp); sleep(1); } } printf("[TH_ID:%ld]: Writing %i \n", pthread_self(),(*(int*)data_exit)); printf("[TH_ID: %ld]: To exit...............\n",pthread_self()); return data_exit; } int main() { int i; pthread_t thread[2]; int i_salir=0; //Number of threads that may exit int thread_rc; if(pthread_mutex_init(&mutex,NULL)!=0) return -1; for (i=0;i<2; i++) { printf("[MAIN:%ld]: Starting............ \n",pthread_self()); if ((thread_rc=pthread_create(&thread[i],NULL,thread_run,&i_salir))!=0) { printf("Error creating the thread. Code %i",thread_rc); return -1; } } sleep(1); printf("[MAIN:%ld]: Thread allocated \n",pthread_self()); sleep(3); //Say to the thread you should exit pthread_mutex_lock(&mutex); i_salir=2; pthread_mutex_unlock(&mutex); int *ptr_output_data; for ( i=0;i<2; i++) { pthread_join(thread[i],(void **)&ptr_output_data); } pthread_mutex_destroy(&mutex); printf("[MAIN:%ld]: Thread pending to return %d \n",pthread_self(), *ptr_output_data); return 0; } |
The proposed solution is based on the following main ideas:
The main
function creates two threads after performing some work waiting for them to finish their execution.
The end signal is made through a variable initialized to zero. As this variable is set to a nonzero number (in this case two) the threads out and decrement the value of this variable.
The newly allocated threads have to periodically check the value of the variable to exit.
Which ends up generating an output where the threads created periodically reprint the following sequence of messages:
[MAIN:2]: Starting............ [MAIN:2]: Starting............ [MAIN:2]: Thread allocated [TH_ID:0]: Hello from the thread [TH_ID:0]: Reading 0 [TH_ID:0]: sleeping 2 seconds, 0 [TH_ID:6]: Hello from the thread [TH_ID:6]: Reading 0 [TH_ID:6]: sleeping 2 seconds, 0 [TH_ID:0]: sleeping 2 seconds, 0 ... [MAIN:2]: Thread pending to return 0
The code also illustrates one of the main problems that addresses the condition variable: the busy waiting. This is to have to recheck periodically the output; i.e., the variable passed as parameter of the function to receive an instruction from another thread to let the thread continue. That type of solution, in principle, is inefficient as it forces the thread periodically recheck that variable. The condition variable provides a more elegant and efficient solution to this issue.