初始化一个条件变量
#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);常量初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;摧毁一个条件变量
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);条件变量阻塞等待
#include <pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 先释放锁 mutex阻塞在 cond 条件变量上超时等待
#include <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); struct timespec {time_t tv_sec; /*second*/ 秒long tv_nsec; /*nanoseconds*/ 纳秒}abstime --- 绝对时间,tv_sec填写的时候 time(NULL)+600 --- 设置超时600s唤醒至少一个阻塞在条件变量cond上的线程
#include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);唤醒阻塞在条件变量cond上的全部线程
#include <pthread.h>int pthread_cond_broadcast(pthread_cond_t *cond);条件变量的作用:避免无必要的竞争
线程虚假唤醒(spurious wakeup)即使没有线程通知条件变量,线程也可能从其等待状态唤醒。
wait方法分为三个操作:
- 释放锁并阻塞
- 等待条件cond发生
- 获取通知后,竞争获取锁
-
wait只有获取到锁以后才会返回
当线程A获取通知后,会竞争获取锁,若锁被线程C获取并改变了条件变量的条件,那么当A重新拿到锁后会继续执行后面的操作,然而此时条件不允许执行后面的操作。
所以,在线程被唤醒后必须重新判断条件是否满足,如果不满足需重新等待;而且,虚假唤醒会重复发生。因此,必须使用while循环。
条件变量只有一种正确的使用方式:
对于wait端:
- 必须与mutex一起使用,该布尔表达式的读写需受此mutex保护。
- 在mutex已上锁的时候才能调用wait()
- 把判断布尔条件和wait()放到while循环中
对于signal/broadcast端:
- 不一定在mutex已上锁的情况下调用signal(理论上)
- 在signal之前一般要修改布尔表达式
- 修改布尔表达式通常要用mutex保护(至少用作full memory barrier)
- 注意区分signal与broadcast:broadcast通常用于表明状态变化,signal通常用于表示资源可用。