5.4 条件变量
5.4.1 条件变量简介
(1)互斥锁的缺点是它只有两种状态:锁定和非锁定。
(2)条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。
(3)条件变量内部是一个等待队列,放置等待的线程,线程在条件变量上等待和通知。互斥锁用于pthread_cond_wait内部对“测试条件”访问控制,条件变量通常和互斥锁一起使用。
(4)条件变量允许线程等待特定条件发生,当条件不满足时,线程通常先进入阻塞状态,等待条件发生变化。一旦其它的某个线程改变了条件,可唤醒一个或多个阻塞的线程。
(5)具体的判断条件(测试条件)还需要用户给出。
(6)条件变量数据类型:pthread_cond_t
5.4.2 条件变量的操作
(1)条件变量的创建和锁毁
|
头文件 |
#include <pthread.h> |
|
函数 |
int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* attr); //初始化 int pthread_cond_destroy(pthread_cond_t* cond); //销毁锁 |
|
返回值 |
成功返回0,否则返回错误编号 |
|
参数 |
cond:条件变量 attr:条件变量属性 |
(2)条件变量的等待操作
|
头文件 |
#include <pthread.h> |
|
函数 |
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex); int pthread_cond_timewait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* timeout); |
|
返回值 |
成功返回0,否则返回错误编号 |
|
参数 |
cond:条件变量 mutex:条件变量属性 timeout:是struct timespec结构体变量。 struct timespec{ time_v tv_sec; //seconds long tv_nsec; //nanoseconds } |
|
备注 |
(1)互斥锁mutex是对条件变量cond和“测试条件”的保护 (2)线程由于调用wait函数阻塞,否则释放互斥锁。 |
(3)条件变量的通知操作
|
头文件 |
#include <pthread.h> |
|
函数 |
int pthread_cond_signal(pthread_cond_t* cond); int pthread_cond_broadcast(pthread_cond_t* cond); |
|
返回值 |
成功返回0,否则返回错误编号 |
|
参数 |
cond:条件变量 |
|
备注 |
(1)pthread_cond_signal函数通知单个线程 (2)pthread_cond_broadcast函数通知所有线程。 |
5.4.3 条件变量使用的注意事项
(1)pthread_cond_wait的内部实现
【附】pthread_cond_wait源码
int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { volatile pthread_descr self = thread_self(); pthread_extricate_if extr; int already_canceled = 0; int spurious_wakeup_count; /* Check whether the mutex is locked and owned by this thread. */ if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP && mutex->__m_owner != self) return EINVAL; /* Set up extrication interface */ extr.pu_object = cond; extr.pu_extricate_func = cond_extricate_func; /* Register extrication interface */ THREAD_SETMEM(self, p_condvar_avail, 0); __pthread_set_own_extricate_if(self, &extr); /* Atomically enqueue thread for waiting, but only if it is not canceled. If the thread is canceled, then it will fall through the suspend call below, and then call pthread_exit without having to worry about whether it is still on the condition variable queue. This depends on pthread_cancel setting p_canceled before calling the extricate function. */ __pthread_lock(&cond->__c_lock, self); if (!(THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) enqueue(&cond->__c_waiting, self); else already_canceled = 1; __pthread_unlock(&cond->__c_lock); if (already_canceled) { __pthread_set_own_extricate_if(self, 0); __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } pthread_mutex_unlock(mutex); spurious_wakeup_count = 0; while (1) { suspend(self); if (THREAD_GETMEM(self, p_condvar_avail) == 0 && (THREAD_GETMEM(self, p_woken_by_cancel) == 0 || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE)) { /* Count resumes that don't belong to us. */ spurious_wakeup_count++; continue; } break; } __pthread_set_own_extricate_if(self, 0); /* Check for cancellation again, to provide correct cancellation point behavior */ if (THREAD_GETMEM(self, p_woken_by_cancel) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { THREAD_SETMEM(self, p_woken_by_cancel, 0); pthread_mutex_lock(mutex); __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* Put back any resumes we caught that don't belong to us. */ while (spurious_wakeup_count--) restart(self); pthread_mutex_lock(mutex); return 0; }