Linux线程同步-----条件变量
来源:互联网 发布:小米平板2怎么连接网络 编辑:程序博客网 时间:2024/04/30 13:51
线程间的同步还有一个情况:进程A 需要等待一个条件成立,才执行,当条件不成立时就阻塞等待 ;进程B 需要设置条件,当条件成立时,唤醒进程A.这里我们就可以用到条件变量。
条件变量变量也是出自POSIX线程标准,另一种线程同步机制,主要用来等待某个条件的发生,然后进行相应的操作,这样可以消除多线程间的竞争。每个条件变量总是和一个互斥量相关联,条件本身是由互斥量保护的,线程在改变条件状态之间必须要锁住互斥量。互斥量是用于上锁,条件变量用于等待。
1. 条件变量的初始化和销毁
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//成功返回0,失败返回错误码
Condition Variable 与Mutex 有类似的初始化函数pthread_cond_init, 当attr 为NULL 就相当于PTHREAD_COND_INITIALIZER
2. 条件变量的相关操作函数
#include <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);int pthread_cond_broadcast(pthread_cond_t *cond);int pthread_cond_signal(pthread_cond_t *cond);
Condtion Variable 总是搭配Mutex 使用,超时条件变量的作用是为了防止某个线程因为异常终止,没有及时释放锁,超时会导致锁因为超时而释放。
3. 条件变量的使用实例
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <pthread.h>#include <errno.h>#include <sys/time.h>static int count = 0;pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;pthread_mutex_t g_cond_lock = PTHREAD_MUTEX_INITIALIZER;void *notify_thx_func(void *arg){ while (1) { sleep(5); pthread_mutex_lock(&g_cond_lock); ++count; pthread_mutex_unlock(&g_cond_lock); printf("please receive data ...\r\n"); pthread_cond_signal(&g_cond); }}void *recv_thx_func(void *arg){ while (1) { pthread_mutex_lock(&g_cond_lock); while (count <= 0) { pthread_cond_wait(&g_cond, &g_cond_lock); } printf("I got the data ...\r\n"); count--; pthread_mutex_unlock(&g_cond_lock); }}int main(int argc, char **argv){ pthread_t tid1, tid2; pthread_create(&tid1, NULL, notify_thx_func, NULL); pthread_create(&tid2, NULL, recv_thx_func, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0;}
4. 关于虚假唤醒 (Spurious wakeups)
互斥器和条件变量用法如下:
pthread_mutex_lock(&lock);while (condition_is_false) { pthread_cond_wait(&cond, &lock);}
上面那个while能换成if吗?答案是不能,否则会导致spurious wakeup虚假唤醒。因为不仅要在pthread_cond_wait前要检查条件是否成立,在pthread_cond_wait之后也要检查。因为pthread_cond_wait不仅能被pthread_cond_signal/pthread_cond_broadcast唤醒,而且还会被其它信号唤醒,后者就是虚假唤醒。
考虑一个生产者消费者队列中的三个线程。
- 线程A正等待从线程安全的队列中读取一个元素。
- 线程B向队列中添加一个元素。在对队列解锁后,但在发出条件信号之前,发生了上下文切换。
- 线程C向队列中添加一个元素,并成功发出条件信号。
- 线程A被唤醒,并处理了上面两个元素。它接着等待被唤醒。
- 线程B恢复现场,并发出条件信号。
- 这时线程A被唤醒,但立即又进入睡眠,因为队列为空。
这个例子很好的解释了虚假唤醒,虽然没有什么副作用,但是却有可能浪费CPU时间。
既然你已经始终需要检查谓词在一个循环,这没有什么区别。 如果基础条件变量可以有其他类型的虚假唤醒。
linux的pthread_cond_wait是用futex系统调用,这个是慢速系统调用,看过apue知道任何慢速系统调用被信号打断的时候会返回 -1,并且把errno置为EINTR,如果慢速系统调用的重启功能被关闭,需要在调用该系统调用的地方手动重启它,像下面这样:
while (1) { int ret = syscall(); if (ret < 0 && errno == EINTR) continue; else break;}
但是futex不能这么用,因为futex结束后到再次重启这个过程有个时间窗,在这个窗口内可能发生了pthread_cond_signal/phread_cond_broadcast,如果发生这种情况,再进行pthread_cond_wait的时候就错过了一次条件变量的变化,就会无限等待下去。但是如果不像上面那样写又无法重启futex系统调用,咋整呢?这就回到了上面检查布尔条件的时候为什么用while而不用if。用while不会因为虚假唤醒而错过phread_cond_signal/pthread_cond_broadcast,而且在通过判断while条件不成立检测出此次唤醒为虚假唤醒并继续调用futex继续等待。
0 0
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量
- linux线程同步--条件变量
- Linux 线程同步---条件变量
- Linux 线程同步---条件变量 .
- Linux 线程同步---条件变量
- Linux线程同步-----条件变量
- linux线程同步:条件变量
- Linux线程同步--条件变量
- linux线程同步---条件变量
- linux线程同步-条件变量
- Linux线程同步(4): 条件变量
- Java-可重入锁
- Python中的MD5
- iOS沙盒访问方式
- 策略模式(strategy)
- ImageView定宽,高度等比例放大
- Linux线程同步-----条件变量
- oracle数据库删除操作
- dagger2简单使用
- 02-变量测试语句
- OpenCV 查找图像轮廓
- 项目之初:Pch文件里的配置.
- Java——操作ProtocolBuffer格式数据初步
- HDU 5775 Bubble Sort (多校4)
- iOS实战演练之——UINavigationBar滑动问题