1.concept
- Conditional variables are another synchronization mechanism available to threads
- Conditional variables provide a meeting place for multiple threads
- When conditional variables are used with mutexes, allow threads to wait for specific conditions to occur in a non-competitive manner
- Conditional variables are things in the thread that wait for a condition to occur, just like signals
2.Use scenarios
- Conditional variables are used with mutexes, and the condition itself is protected by the mutex. Threads must lock the mutex before changing the condition state
- Other threads will not notice this change until they get the mutex, because the mutex must be locked before the condition can be evaluated
3.data type
- pthread_cond_t
4.Initialization and Release of Conditional Variables
1. Static Initialization
- Set the condition variable defined by pthread_cond_t directly to the constant PTHREAD_COND_INITIALIZER
- Static initialization conditional variables can only have default conditional variable attributes and cannot set other conditional variable attributes
pthread_cond_t cond; cond=PTHREAD_COND_INITIALIZER; //perhaps pthread_cond_t *cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t)); *cond=PTHREAD_COND_INITIALIZER;
Dynamic Initialization
- Static initialization conditional variables can only have default conditional variable properties. We can dynamically initialize conditional variables through the pthread_mutex_init function, and we can select the properties to set conditional variables when initializing
#include <pthread.h> int pthread_cond_init(pthread_cond_t* restrict cond,const pthread_condattr_t* restrict attr); int pthread_cond_destroy(pthread_cond_t* cond); //Return value: success returns 0; failure returns error number
pthread_cond_init:
- Function: Initialize conditional variables
- Parameters:
- Parameter 1: Conditional variable to initialize
- Parameter 2: The properties of the conditional variable at initialization. If the default properties are used, fill in NULL here
pthread_cond_destroy:
- Function: De-initialize conditional variables (before conditional variables release memory)
- Parameter: Conditional variable
- NOTE (IMPORTANT): This function only reinitializes the mutex and does not free up memory space. If the mutex is requested through a function such as malloc, the pthread_mutex_destroy function needs to be called before the mutex can be freely removed.
pthread_cond_t cond; pthread_cond_init(&cond,NULL); /*do something*/ pthread_cond_destroy(&cond);
pthread_cond_t * cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t)); pthread_cond_init(cond,NULL); /*do something*/ pthread_cond_destroy(cond); free(cond);
5.Waiting condition variable
#include <pthread.h> int pthread_cond_wait(pthread_cond_t* restrict cond,pthread_mutex_t* restrict mutex); int pthread_cond_timedwait(pthread_cond_t* cond,pthread_mutex_t* restrict mutex,const struct timespec* restrict tsptr); //Return value: success returns 0; failure returns error number
- When these two function calls return successfully, the thread needs to recalculate the condition because another thread may already be running and changing the condition
pthread_cond_wait
-
Note: Wait for condition variable to become true
-
How to use: The mutex mutex mutex mutex is locked in advance, and then the mutex protects the condition until the parameter 1cond condition variable becomes true. This function remains blocked while waiting for the condition variable to become true. However, when blocked, the mutex mutex mutex is unlocked (because other threads need to use this lock to make the condition variable true)
-
When the pthread_cond_wait function returns, the mutex is locked again
pthread_cond_timedwait
- The pthread_cond_timedwait function functions the same as the pthread_cond_wait function. However, there is one more timeout parameter. The timeout value specifies how long we are willing to wait, which is represented by the timespec structure
- If the condition does not appear after the time-out expires, the function retrieves the mutex and returns the error ETIMEOUT
- Note: This time value is an absolute number rather than a relative number. For example, if you are willing to wait for three minutes, instead of converting three minutes into a timespec structure, you need to add three minutes to the current time before converting to a timespec structure
The following defines an absolute time-out function to get the timeout value:
- You can use the clock_gettime function to get the current time represented by the timespec structure. However, not all platforms currently support this function. Therefore, you can use gettimeofday to get the current time represented by the TimeValue structure and then convert this time to the timespec structure.
#include <sys/time.h> #include <stdlib.h> void maketimeout(struct timespec *tsp, long minutes) { struct timeval now; /* get the current time */ gettimeofday(&now, NULL); tsp->tv_sec = now.tv_sec; tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */ /* add the offset to get timeout value */ tsp->tv_sec += minutes * 60; }
6.Conditional variable signaling
#include <pthread.h> int pthread_cond_signal(pthread_cond_t* cond); int pthread_cond_broadcast(pthread_cond_t* cond); //Return value: success returns 0; failure returns error number
- These two functions are used to notify threads that condition variables have met (become true). When these two functions are called, they signal the threads or conditions
- It must be noted that the thread must be signaled after changing the conditional state
- pthread_cond_signal function: can wake at least one thread waiting for the condition
- pthread_cond_broad function: wakes up all threads waiting for the condition
7.Example
#include <pthread.h> struct msg { struct msg *m_next; /* ... more stuff here ... */ }; struct msg *workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; void process_msg(void) { struct msg *mp; for (;;) { pthread_mutex_lock(&qlock); while (workq == NULL) pthread_cond_wait(&qready, &qlock); mp = workq; workq = mp->m_next; pthread_mutex_unlock(&qlock); /* now process the message mp */ } } void enqueue_msg(struct msg *mp) { pthread_mutex_lock(&qlock); mp->m_next = workq; workq = mp; pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready); }
8.case
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <strings.h> #define BUFFSIZE 2 struct prodcons { char buff[BUFFSIZE]; int write_index; int read_index; pthread_cond_t notempty; pthread_cond_t notfull; pthread_mutex_t lock; }; void init(struct prodcons *pro); void* producer_func(void* arg); void* customer_func(void* arg); void put(struct prodcons *pro,int data); int get(struct prodcons *pro); int main() { pthread_t producer,customer; struct prodcons pro; init(&pro); if(pthread_create(&producer,NULL,producer_func,&pro)!=0){ perror("pthread_create"); exit(EXIT_FAILURE); } if(pthread_create(&customer,NULL,customer_func,&pro)!=0){ perror("pthread_create"); exit(EXIT_FAILURE); } if(pthread_join(producer,NULL)!=0){ perror("pthread_join"); exit(EXIT_FAILURE); } if(pthread_join(customer,NULL)!=0){ perror("pthread_join"); exit(EXIT_FAILURE); } exit(0); } void init(struct prodcons *pro) { bzero(pro->buff,sizeof(pro->buff)); pro->write_index=0; pro->read_index=0; if((pthread_cond_init(&pro->notempty,NULL)!=0) ||(pthread_cond_init(&pro->notfull,NULL)!=0) ||(pthread_mutex_init(&pro->lock,NULL)!=0)){ perror("init"); exit(EXIT_FAILURE); } } void* producer_func(void* arg) { int i; for(i=1;i<=5;++i) { printf("producer sleep 1 second to produce..\n"); sleep(1); printf("put %d to produce\n",i); put(arg,i); } for(i=6;i<=10;++i) { printf("producer sleep 3 second to produce..\n"); sleep(3); printf("produce:put %d\n",i); put(arg,i); } put(arg, -1); printf("producer exit\n"); pthread_exit(0); } void* customer_func(void* arg) { int data; while(1) { printf("customer wait 2 second to consumption.\n"); sleep(2); data=get(arg); printf("customer:get %d\n",data); if(data==-1) break; } printf("customer exit\n"); pthread_exit(0); } void put(struct prodcons *pro,int data) { pthread_mutex_lock(&pro->lock); //If full, wait until the non-full condition variable is generated while(((pro->write_index+1)%BUFFSIZE) == pro->read_index) { printf("producer is full,wait not full"); pthread_cond_wait(&pro->notfull,&pro->lock); } pro->buff[pro->write_index]=data; pro->write_index++; if(pro->write_index>=BUFFSIZE) pro->write_index=0; pthread_cond_signal(&pro->notempty); pthread_mutex_unlock(&pro->lock); } int get(struct prodcons *pro) { int data; pthread_mutex_lock(&pro->lock); //If empty, wait for non-empty while(pro->write_index==pro->read_index) { printf("customer is empty,wait not empty\n"); pthread_cond_wait(&pro->notempty,&pro->lock); } data=pro->buff[pro->read_index]; pro->read_index++; if(pro->read_index>=BUFFSIZE) pro->read_index=0; pthread_cond_signal(&pro->notfull); pthread_mutex_unlock(&pro->lock); return data; }