四十一、Linux 线程——线程同步之条件变量
41.1 概念
41.1.1 条件变量的介绍
- 互斥锁的缺点是它只有两种状态:锁定和非锁定
- 条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足
- 条件变量内部是一个等待队列,放置等待的线程,线程在条件变量上等待和通知,互斥锁用来保护等待队列(对等待队列上锁),条件变量通常和互斥锁一起使用
- 条件变量允许线程等待特定条件发生,当条件不满足时,线程通常先进入阻塞状态,等待条件发生变化。一旦其它的某个线程改变了条件,可唤醒一个或多个阻塞的线程
- 具体的判断条件还需用户给出
- 条件变量数据类型: pthread_cond_t
41.1.2 条件变量创建和销毁
1 #include <pthread.h> 2 int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr); 3 int pthread_cond_destroy(pthread_cond_t *cond);
- 函数参数:
- cond:条件变量
- attr:条件变量属性
- 返回值:
- 成功,返回 0;出错,返回错误编号
41.1.3 条件变量等待操作
1 #include <pthread.h> 2 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 3 int pthread_cond_timewait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout); 4 5 struct timespec { 6 time_t tv_sec; /** seconds */ 7 ong tv_nsec; /** nanoseconds */ 8 };
- 函数参数:
- cond:条件变量
- mutex:互斥锁
- 返回值:成功,返回 0,出错,返回错误编号
- 互斥锁 mutex 是对条件变量 cond 的保护
- 线程由于调用 wait 函数阻塞,否则释放互斥锁
41.1.4 条件变量通知操作
1 #include <pthread.h> 2 int pthread_cond_signal(pthread_cond_t *cond); 3 int pthread_cond_broadcast(pthread_cond_t *cond);
- 函数参数:
- cond:条件变量
- 返回值:成功,返回 0;失败,返回错误编号
- 当条件满足,线程需要通知等待的线程
- pthread_cond_signal 函数通知单个线程
- pthread_cond_broadcast 函数通知所有线程
41.1.5 pthread_cond_wait(cond, mutex) 函数内部流程
- unlock(&mutex):释放锁
- lock(&mutex)
- 将线程自己插入到条件变量的等待队列中
- unlock(&mutex);
- 当前等待的线程阻塞 <<== 等待其他线程通知唤醒(signal 或 broadcast)
- 在唤醒后,lock(&mutex)
- 从等待队列中删除线程自己
41.2 例子
一个线程负责计算结果,一个线程负责获取结果
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <unistd.h> 5 6 /** 两个线程定义的共享资源 */ 7 typedef struct { 8 int res; 9 int is_wait; ///< 用户给出的用于判断的条件 10 pthread_cond_t cond; ///< 条件变量 11 pthread_mutex_t mutex; ///< 互斥锁 12 }Result; 13 14 15 /** 计算并将结果放置在 Result 中的线程运行函数 */ 16 void *set_fn(void *arg) 17 { 18 Result *r = (Result *)arg; 19 int i = 0; 20 int sum = 0; 21 22 for(; i <= 100; i++){ 23 sum += i; 24 } 25 26 /** 将结果放置到 Result 中 */ 27 r->res = sum; 28 29 pthread_mutex_lock(&r->mutex); 30 /** 判断获取结果的线程是否准备好 */ 31 while(!r->is_wait){ 32 pthread_mutex_unlock(&r->mutex); 33 usleep(100); 34 pthread_mutex_lock(&r->mutex); 35 } 36 pthread_mutex_unlock(&r->mutex); 37 38 /** 通知唤醒等待的那个获取结果的线程 */ 39 pthread_cond_broadcast(&r->cond); 40 41 return (void *)0; 42 } 43 44 /** 获得结果的线程运行函数 */ 45 void *get_fn(void *arg) 46 { 47 Result *r = (Result *)arg; 48 49 /** 对两个线程共享的判断条件进行保护(加锁) */ 50 /** 两个线程对判断条件的操作是互斥的 */ 51 pthread_mutex_lock(&r->mutex); 52 /** 当线程启动后,将此变量设置为1,代表此线程已经准备好了 */ 53 r->is_wait = 1; 54 55 /** 获取结果的线程等待 */ 56 pthread_cond_wait(&r->cond, &r->mutex); 57 58 /** 被唤醒后 */ 59 pthread_mutex_unlock(&r->mutex); 60 61 /** 去获取计算结果 */ 62 int res = r->res; 63 printf("0x%lx get sum is %d\n", pthread_self(), res); 64 65 return (void *)0; 66 } 67 68 int main(void) 69 { 70 int err; 71 pthread_t cal, get; 72 73 Result r; 74 r.is_wait = 0; 75 pthread_cond_init(&r.cond, NULL); 76 pthread_mutex_init(&r.mutex, NULL); 77 78 /** 启动获取结果的线程 */ 79 if((err = pthread_create(&get, NULL, get_fn, (void *)&r)) != 0){ 80 perror("pthread create error"); 81 } 82 83 /** 启动计算结果的线程 */ 84 if((err = pthread_create(&cal, NULL, set_fn, (void *)&r)) != 0){ 85 perror("pthread create error"); 86 } 87 88 pthread_join(cal, NULL); 89 pthread_join(get, NULL); 90 91 pthread_cond_destroy(&r.cond); 92 pthread_mutex_destroy(&r.mutex); 93 94 pthread_cond_destroy(&r.cond); 95 pthread_mutex_destroy(&r.mutex); 96 return 0; 97 }
编译执行结果: