深入解析条件变量(condition variables)
深入解析条件变量#
什么是条件变量(condition variables)##
引用APUE中的一句话:
Condition variables are another synchronization mechanism available to threads.
These synchronization objects provide a place for threads to rendezvous. When used with mutexes, condition variables allow threads to wait in a race-free way for arbitrary conditions to occur.
条件变量是线程的另外一种同步机制,这些同步对象为线程提供了会合的场所,理解起来就是两个(或者多个)线程需要碰头(或者说进行交互-一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则接收条件已经发生改变的信号。
条件变量同锁一起使用使得线程可以以一种无竞争的方式等待任意条件的发生。所谓无竞争就是,条件改变这个信号会发送到所有等待这个信号的线程。而不是说一个线程接受到这个消息而其它线程就接收不到了。
一个例子##
具体的函数介绍就不说了,详细参考APUE,下面通过一个例子来详细说一下正确使用条件变量的方法。下例实现了生产者和消费者模型,生产者向队列中插入数据,消费者则在生产者发出队列准备好(有数据了)后接收消息,然后取出数据进行处理。实现的关键点在以下几个方面:
- 生产者和消费者都对条件变量的使用加了锁
- 消费者调用pthread_cond_wait,等待队列是否准备好的信息,注意参数有两个,一个是pthread_cond_t,另外一个是pthread_mutex_t.
代码:
#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);
}
关于上面例子的几个疑问##
为什么pthread_cond_wait需要加锁??###
pthread_cond_wait中的mutex用于保护条件变量,调用这个函数进行等待条件的发生时,mutex会被自动释放,以供其它线程(生产者)改变条件,pthread_cond_wait中的两个步骤必须是原子性的(atomically,万恶的APUE中文版把这个单词翻译成了『自动』,误人子弟啊),也就是说必须把两个步骤捆绑到一起:
- 把调用线程放到条件等待队列上
- 释放mutex
不然呢,如果不是原子性的,上面的两个步骤中间就可能插入其它操作。比如,如果先释放mutex,这时候生产者线程向队列中添加数据,然后signal,之后消费者线程才去『把调用线程放到等待队列上』,signal信号就这样被丢失了。
如果先把调用线程放到条件等待队列上,这时候另外一个线程发送了pthread_cond_signal(我们知道这个函数的调用是不需要mutex的),然后调用线程立即获取mutex,两次获取mutex会产生deadlock.
在生产者线程中修改条件时为什么要加mutex??###
如果不这么做信号可能会丢失,看下面的例子:
Thead A Thread B
pthread_mutex_lock(&qlock);
while (workq == NULL)
mp->m_next = workq;
workq = mp;
pthread_cond_signal(&cond);
pthread_cond_wait(&qready, &qlock);
在while判断之后向队列中插入数据,虽然已经有数据了,但线程A还是调用了pthread_cond_wait等待下一个信号到来。。
消费者线程中判断条件为什么要放在while中??###
while (workq == NULL)
pthread_cond_wait(&qready, &qlock);
mp = workq;
我们把while换成if可不可以呢?
if (workq == NULL)
pthread_cond_wait(&qready, &qlock);
mp = workq;
答案是不可以,一个生产者可能对应着多个消费者,生产者向队列中插入一条数据之后发出signal,然后各个消费者线程的pthread_cond_wait获取mutex后返回,当然,这里只有一个线程获取到了mutex,然后进行处理,其它线程会pending在这里,处理线程处理完毕之后释放mutex,刚才等待的线程中有一个获取mutex,如果这里用if,就会在当前队列为空的状态下继续往下处理,这显然是不合理的。
signal到底是放在unlock之前还是之后??###
void
enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&qready);
}
如果先unlock,再signal,如果这时候有一个消费者线程恰好获取mutex,然后进入条件判断,这里就会判断成功,从而跳过pthread_cond_wait,下面的signal就会不起作用;另外一种情况,一个优先级更低的不需要条件判断的线程正好也需要这个mutex,这时候就会转去执行这个优先级低的线程,就违背了设计的初衷。
void
enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_cond_signal(&qready);
pthread_mutex_unlock(&qlock);
}
如果把signal放在unlock之前,消费者线程会被唤醒,获取mutex发现获取不到,就又去sleep了。浪费了资源.但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。
所以在Linux中推荐使用这种模式。
References:
why pthread_cond_wait need an lock?
Calling pthread_cond_signal without locking mutex
Why do pthreads’ condition variable functions require a mutex?
pthread_cond_signal 和 pthread_mutex_unlock顺序问题
深入解析条件变量(condition variables)的更多相关文章
- [development][C] 条件变量(condition variables)的应用场景是什么
产生这个问题的起因是这样的: [:] <tong> lilydjwg: 主线程要启动N个子线程, 一个局部变量作为把同样的参数传入每一个子线程. 子线程在开始的十行会处理完参数. ...
- Linux多线程编程详细解析----条件变量 pthread_cond_t
Linux操作系统下的多线程编程详细解析----条件变量 1.初始化条件变量pthread_cond_init #include <pthread.h> int pthread_cond_ ...
- python线程条件变量Condition(31)
对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...
- Linux组件封装(二)中条件变量Condition的封装
条件变量主要用于实现线程之间的协作关系. pthread_cond_t常用的操作有: int pthread_cond_init(pthread_cond_t *cond, pthread_conda ...
- python线程的条件变量Condition的用法实例
Condition 对象就是条件变量,它总是与某种锁相关联,可以是外部传入的锁或是系统默认创建的锁.当几个条件变量共享一个锁时,你就应该自己传入一个锁.这个锁不需要你操心,Condition 类会 ...
- [转] 条件变量(Condition Variable)详解
http://www.wuzesheng.com/?p=1668 条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法.举个简单的例子,应用程序A ...
- Linux操作系统下的多线程编程详细解析----条件变量
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法,弥补了互斥锁(Mutex)的不足. 1.初始化条件变量pthread_cond_init #include <pthread.h> ...
- 条件变量(Condition Variable)详解
条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法.举个简单的例子,应用程序A中包含两个线程t1和t2.t1需要在bool变量test_cond ...
- java 并发时使用条件变量--Condition
lock--unlock的方式在实际中使用较少,一般使用synchronized获取对象的内部锁替代,但是lock--unlock对了解synchronized有很大的帮助. 创建一个bank对象用于 ...
随机推荐
- mvc get image 500
mvc中get图片时报500错误 很奇怪,突然变500错误了,一番查找之后发现原来是因为mimeType重复定义的原因,吐血.. http://stackoverflow.com/questions/ ...
- Spark DataFrame写入HBase的常用方式
Spark是目前最流行的分布式计算框架,而HBase则是在HDFS之上的列式分布式存储引擎,基于Spark做离线或者实时计算,数据结果保存在HBase中是目前很流行的做法.例如用户画像.单品画像.推荐 ...
- bzoj 3576[Hnoi2014]江南乐 sg函数+分块预处理
3576: [Hnoi2014]江南乐 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 1929 Solved: 686[Submit][Status ...
- 对于 @Autowired注解和@service注解的理解
@Autowired相当于Spring自动给你进行了new一个对象将这个对象放入你的注解所在类里面. @service 是可以让IOC容器对于你注解的类可以在容器中生成相应的bean实例 便于我们进行 ...
- eclipse导入/编译hadoop源代码
1. 确保安装好JDK和eclipse 详细教程见: http://blog.csdn.net/kangdakangdaa/article/details/11364985 2. 安装 Subclip ...
- 【前端】诸葛io收集前端js报错信息
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/zhuge_error.html 一.什么是诸葛io 诸葛io就是通过分析用户的操作事件对用户数据,行为路径等进行分 ...
- dm642的视频口输出
void VP1_EDMA(int displayMode,unsigned int w,unsigned int h) { unsigned int i=0,k=0; EDMA_Hand ...
- PCI9054芯片的型号说明及购买建议
个脚,这也是大部分人用到的:而"BI"结尾的是BGA封装的,225个脚,较少用到,对它不再多说. 这几种系列量产的时间如下: 年11月 年8月 年2月 年 年 年到2006年期间, ...
- windows2003服务器系统日志:查看电脑远程登录记录
控制面板>>管理工具>>事件查看器>>选择安全性再点工具栏目中查看>>筛选>>事件ID填528进行过滤,时间你看是多久,双击查看之后就可以找 ...
- APACHE服务器出现No input file specified.的完美解决方案
启用REWRITE的伪静态功能的时候,首页可以访问,而访问内页的时候,就提示:“No input file specified.” 原因在于使用的PHP是fast_cgi模式,而在某些情况下,不能正确 ...