深入理解pthread_cond_wait、pthread_cond_signal
===============================man pthread_cond_wait的解释==========================
LINUX环境下多线程编程肯定会遇到需要条件变量的情况,此时必然要使用pthread_cond_wait()函数。但这个函数的执行过程比较难于理解。
pthread_cond_wait()的工作流程如下(以MAN中的EXAMPLE为例):
Consider two shared variables x and y, protected by the mutex mut, and a condition vari-
able cond that is to be signaled whenever x becomes greater than y.
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Waiting until x is greater than y is performed as follows:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
Modifications on x and y that may cause x to become greater than y should signal the con-
dition if needed:
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
这个例子的意思是,两个线程要修改X和 Y的值,第一个线程当X<=Y时就挂起,直到X>Y时才继续执行(由第二个线程可能会修改X,Y的值,当X>Y时唤醒第一个线程),即 首先初始化一个普通互斥量mut和一个条件变量cond。之后分别在两个线程中分别执行如下函数体:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
和: pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mut);
其实函数的执行过程非常简单,在第一个线程执行到pthread_cond_wait(&cond,&mut)时,此时如果X<=Y,则此函数就将mut互斥量解锁 ,再将cond条件变量加锁 ,此时第一个线程挂起 (不占用任何CPU周期)。
而在第二个线程中,本来因为mut被第一个线程锁住而阻塞,此时因为mut已经释放,所以可以获得锁mut,并且进行修改X和Y的值,在修改之后,一个IF语句判定是不是X>Y,如果是,则此时pthread_cond_signal()函数会唤醒第一个线程 ,并在下一句中释放互斥量mut。然后第一个线程开始从pthread_cond_wait()执行,首先要再次锁mut , 如果锁成功,再进行条件的判断 (至于为什么用WHILE,即在被唤醒之后还要再判断,后面有原因分析),如果满足条件,则被唤醒 进行处理,最后释放互斥量mut 。
至于为什么在被唤醒之后还要再次进行条件判断(即为什么要使用while循环来判断条件),是因为可能有“惊群效应”。有人觉得此处既然是被唤醒的,肯定 是满足条件了,其实不然。如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。 对此,转来一段:
引用下POSIX的RATIONALE:
Condition Wait Semantics
It is important to note that when pthread_cond_wait() and pthread_cond_timedwait() return without error, the associated predicate may still be false. Similarly, when pthread_cond_timedwait() returns with the timeout error, the associated predicate may be true due to an unavoidable race between the expiration of the timeout and the predicate state change.
The application needs to recheck the predicate on any return because it cannot be sure there is another thread waiting on the thread to handle the signal, and if there is not then the signal is lost. The burden is on the application to check the predicate.
Some implementations, particularly on a multi-processor, may sometimes cause multiple threads to wake up when the condition variable is signaled simultaneously on different processors.
In general, whenever a condition wait returns, the thread has to re-evaluate the predicate associated with the condition wait to determine whether it can safely proceed, should wait again, or should declare a timeout. A return from the wait does not imply that the associated predicate is either true or false.
It is thus recommended that a condition wait be enclosed in the equivalent of a "while loop" that checks the predicate.
从上文可以看出:
1,pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,while循环的意义就体现在这里了,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上 的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.
2,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐此处使用while循环.
其实说白了很简单,就是pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话,就必须要使用while来进行条件判断,以保证临界区内只有一个线程在处理。
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或pthread_cond_broadcast来唤醒它 。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait() 函数一进入wait状态就会自动release mutex。当其他线程通过 pthread_cond_signal() 或pthread_cond_broadcast ,把该线程唤醒,使 pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex 。
pthread_cond_signal 函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
但是 pthread_cond_signal 在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,
---------------------
yeyuangen
深入理解pthread_cond_wait、pthread_cond_signal的更多相关文章
- 线程相关函数(6)-pthread_cond_wait(),pthread_cond_signal(), 条件变量
pthread_cond_tpthread_cond_initpthread_cond_destroypthread_cond_waitpthread_cond_timedwaitpthread_co ...
- NPTL 线程同步方式
NPTL提供了互斥体 pthread_mutex_t 类型进行线程同步,防止由于多线程并发对全局变量造成的不正确操作.使用 pthread_mutext_t 对数据进行保护已经可以实现基本的数据同步, ...
- (52)LINUX应用编程和网络编程之七Linux线程
3.7.1.再论进程 3.7.1.1.多进程实现同时读取键盘和鼠标 3.7.1.2.使用进程技术的优势 (1)CPU时分复用,单核心CPU可以实现宏观上的并行.微观上的串行 (2)实现多任务系统需求( ...
- JVM源码分析之Object.wait/notify(All)完全解读
概述 本文其实一直都想写,因为各种原因一直拖着没写,直到开公众号的第一天,有朋友再次问到这个问题,这次让我静心下来准备写下这篇文章,本文有些东西是我自己的理解,比如为什么JDK一开始要这么设计,初衷是 ...
- LINUX 线程
1.使用进程技术的优势(1)CPU时分复用,单核心CPU可以实现宏观上的并行(2)实现多任务系统需求(多任务的需求是客观的)2.进程技术的劣势(1)进程间切换开销大(2)进程间通信麻烦而且效率低3.解 ...
- 多线程编程中条件变量和的spurious wakeup 虚假唤醒
1. 概述 条件变量(condition variable)是利用共享的变量进行线程之间同步的一种机制.典型的场景包括生产者-消费者模型,线程池实现等. 对条件变量的使用包括两个动作: 1) 线程等待 ...
- 【Java并发专题之三】Java线程互斥、协作原理
(I)Java线程互斥原理之synchronized原理 从JDK5引入CAS原子操作,但没有对synchronized关键字做优化,而是增加了J.U.C.concurrent,concurrent包 ...
- 深入并发锁,解析Synchronized锁升级
这篇文章分为六个部分,不同特性的锁分类,并发锁的不同设计,Synchronized中的锁升级,ReentrantLock和ReadWriteLock的应用,帮助你梳理 Java 并发锁及相关的操作. ...
- Linux-线程同步之互斥锁
1.互斥锁又叫互斥量(mutex) 2.相关函数:pthread_mutex_init pthread_mutex_destroy pthread_mutex_lock pthread_mute ...
随机推荐
- iOS UITextField 响应键盘的return 事件
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(, , , )] textField.returnKeyT ...
- 【转载】FPGA算法设计随笔
FPGA设计算法依次需要完成MATLAB浮点仿真 MATLAB定点仿真 verilogHDL定点运算以及数据对比的流程.其中浮点到定点的转换尤为重要,需要在数据表示范围和精度之间做出权衡.另外掌握定点 ...
- awk、grep、sed是linux操作文本的三大利器,也是必须掌握的linux命令之一
awk.grep.sed是linux操作文本的三大利器,也是必须掌握的linux命令之一.三者的功能都是处理文本,但侧重点各不相同,其中属awk功能最强大,但也最复杂.grep更适合单纯的查找或匹配文 ...
- ibm z14大型主机介绍
IBM z14™大型主机 (z14)被设计为数字经济中值得信任的基础架构.它提供 特性和功能以满足对于新服务和更佳客户体验的需求,同时保护日益 增长的数据量,并遵从日益复杂的法规.IBM z14 ...
- Python基础——8错误、调试和测试
捕捉错误 try: print('try...') r = 10 / int('2') print('result:', r) except ValueError as e: print('Value ...
- 环形链表得golang实现
给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. 输入:head = ...
- Git 生成SSH Key
背景:服务器是LINUX系统(centos7),使用GitLab管理git代码库.各个客户端通过sourcetree 工具,采用SSH获取.提交代码.使用SSH的方式需要公钥和私钥.下面介绍秘钥的生成 ...
- Nginx 的 access log 如何以 json 形式记录?
Nginx 的 access log 默认是以空格分隔的字符串形式记录的,格式如下 log_format proxy '[$time_local] $remote_addr ' '$protocol ...
- UVALive - 5135 - Mining Your Own Business(双连通分量+思维)
Problem UVALive - 5135 - Mining Your Own Business Time Limit: 5000 mSec Problem Description John D ...
- (二)Basic Concepts 基本概念
Basic Concepts There are a few concepts that are core to Elasticsearch. Understanding these concepts ...