多线程编程中条件变量和虚假唤醒的讨论
1. 概述
条件变量(condition variable)是利用共享的变量进行线程之间同步的一种机制。典型的场景包括生产者-消费者模型,线程池实现等。
对条件变量的使用包括两个动作:
1) 线程等待某个条件, 条件为真则继续执行,条件为假则将自己挂起(避免busy wait,节省CPU资源);
2) 线程执行某些处理之后,条件成立;则通知等待该条件的线程继续执行。
3) 为了防止race-condition,条件变量总是和互斥锁变量mutex结合在一起使用。
一般的编程模式:
var mutex;
var cond;
var something;
Thread1: (等待线程)
lock(mutex);
while( something not true ){
condition_wait( cond, mutex);
}
do(something);
unlock(mutex);
//============================
Thread2: (解锁线程)
do(something);
....
something = true;
unlock(mutex);
condition_signal(cond);
函数说明:
(1) Condition_wait():调用时当前线程立即进入睡眠状态,同时互斥变量mutex解锁(这两步操作是原子的,不可分割),以便其它线程能进入临界区修改变量。
(2) Condition_signal(): 线程调用此函数后,除了当前线程继续往下执行以外; 操作系统同时做如下动作:从condition_wait()中进入睡眠的线程中选一个线程唤醒, 同时被唤醒的线程试图锁(lock)住互斥量mutex, 当成功锁住后,线程就从condition_wait()中成功返回了。
2. 函数接口
pthread: pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast()
Java: Condition.await()/Condition.signal()/Condition.signalAll()
3. 虚假唤醒(spurious wakeup)在采用条件等待时,我们使用的是
while(条件不满足){
condition_wait(cond, mutex);
}
而不是:
If( 条件不满足 ){
Condition_wait(cond,mutex);
}
这是因为可能会存在虚假唤醒”spurious wakeup”的情况。
也就是说,即使没有线程调用condition_signal, 原先调用condition_wait的函数也可能会返回。此时线程被唤醒了,但是条件并不满足,这个时候如果不对条件进行检查而往下执行,就可能会导致后续的处理出现错误。
虚假唤醒在linux的多处理器系统中/在程序接收到信号时可能回发生。在Windows系统和JAVA虚拟机上也存在。在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。
注意:即使是虚假唤醒的情况,线程也是在成功锁住mutex后才能从condition_wait()中返回。即使存在多个线程被虚假唤醒,但是也只能是一个线程一个线程的顺序执行,也即:lock(mutex) 检查/处理 condition_wai()或者unlock(mutex)来解锁.
4. 解锁和等待转移(wait morphing)
解锁互斥量mutex和发出唤醒信号condition_signal是两个单独的操作,那么就存在一个顺序的问题。谁先随后可能会产生不同的结果。如下:
[color=red](1) 按照 unlock(mutex); condition_signal()顺序, 当等待的线程被唤醒时,因为mutex已经解锁,因此被唤醒的线程很容易就锁住了mutex然后从conditon_wait()中返回了。
//...
unlock(mutex);
condition_signal(cond);
(2) 按照 condition_signal(); unlock(mutext)顺序,当等待线程被唤醒时,它试图锁住mutex,但是如果此时mutex还未解锁,则线程又进入睡眠,mutex成功解锁后,此线程在再次被唤醒并锁住mutex,从而从condition_wait()中返回。
//...
condition_signal(cond);
unlock(mutex);
[/color]
可以看到,按照(2)的顺序,对等待线程可能会发生2次的上下文切换,严重影响性能。因此在后来的实现中,对(2)的情况,如果线程被唤醒但是不能锁住mutex,则线程被转移(morphing)到互斥量mutex的等待队列中,避免了上下文的切换造成的开销。 --
wait morphing
编程时,
推荐采用(1)的顺序解锁和发唤醒信号。
而Java编程只能按照(2)的顺序,否则发生异常!!。
在SUSv3
http://en.wikipedia.org/wiki/Single_UNIX_Specification的规范中(pthread),指明了这两种顺序不管采用哪种,其实现效果都是一样的。
分享到:
相关推荐
在Java并发编程中,尤其是在处理多线程同步时,一个重要的概念是“虚假唤醒”(Spurious Wakeup)。这是一种较为罕见的现象,但在实际开发过程中仍然值得高度关注。虚假唤醒指的是,在没有其他线程调用`notify()`...
在Linux多线程编程中,`pthread_cond_wait()`是一个关键的同步原语,它用于线程间的通信和协作。在使用`pthread_cond_wait()`时,通常会在调用它之前加入一个while循环来判断条件是否满足。这个做法是为了避免虚假...
在Java多线程编程中,条件变量是实现线程之间协作的一种同步机制。条件变量允许线程在某个条件不满足时挂起,直到其他线程改变条件并发出信号。在Java中,从JDK 1.5版本开始,引入了java.util.concurrent.locks....
* 虚假唤醒(Spurious Wakeup):Condition_Variable 的 wait() 函数可能会在没有通知的情况下返回,这被称为虚假唤醒。 * 惊群效应(Thundering Herd):Condition_Variable 的 notify_all() 函数可能会导致所有...
总之,“6_pthread.rar”压缩包为学习和实践Linux C语言下的线程操作提供了宝贵的资料,特别是条件变量和互斥锁的使用,这将有助于提升你的多线程编程技能。通过理解和应用这些概念,你将能更好地应对复杂的并发问题...
虚假唤醒(Spurious Wakeup)是指线程在没有被`notify()`或`notifyAll()`的情况下从`wait()`状态中醒来。这是Java并发编程中一个重要的概念,因为根据JVM规范,`wait()`可能会发生虚假唤醒,尽管这在实际应用中很少...
请注意,正确地使用条件变量和互斥锁需要遵循一些最佳实践,比如“避免假唤醒”(spurious wakeups)和“避免死锁”(deadlocks)。在实际应用中,可能还需要结合其他同步原语,如信号量(semaphores)或屏障...
在编程领域,尤其是在多线程编程中,`QWaitCondition` 是一个非常重要的概念,尤其在使用Qt库进行跨平台开发时。`QWaitCondition` 是Qt提供的一种同步原语,它允许线程等待特定条件的发生,而这个条件通常是由其他...
它在多线程环境中,允许一个生产者线程添加元素,同时一个消费者线程取出元素,而无需使用传统的锁机制,从而提高性能并降低竞态条件的可能性。 在传统的并发队列实现中,通常会使用互斥锁(mutex)来保护共享数据...
虚假的Spurious是一个工具集,允许在本地对一部分AWS资源进行开发。 这些服务作为Docker容器运行,Spurious管理它们的生命周期和链接,因此您只需要担心使用这些服务。 要使用Spurious,您需要将每个AWS服务的终结点...
### 理解无寄生动态范围在宽带GSPS ADC中的应用 #### 一、引言 在选择用于高性能系统的宽带模拟到数字转换器(ADC)时,必须考虑多种模拟输入规格,其中包括分辨率、采样率、信噪比(SNR)、有效位数(ENOB)、...
【杂散发射(Spurious Emission)与带外发射(Out of Band Emission)的区别】 杂散发射是指在必要带宽之外的特定频率上出现的发射现象,这些发射的强度可以降低,但不会影响到相应的信息传递。它包括谐波发射、...
文档中讨论了x86/x64 CPU架构的若干安全相关的技术细节,包括: - **权限级别**:文档提到了"Traditional"的权限级别,这是指操作系统的分层权限机制。在x86架构中,这通常是通过ring0至ring3来划分内核模式与用户...
Java中的`Object`类提供了`wait()`方法,这是Java多线程编程中一个至关重要的功能,它与`notify()`和`notifyAll()`一起用于线程间的协作。`wait()`方法主要用于实现线程的等待和唤醒机制,允许线程在特定条件下暂停...
这种情况称为虚假回归或伪回归(spurious regression)。 单位根检验是检验数据平稳性的常用方法。单位根检验的目的是为了检测时间序列中是否存在单位根。单位根是指时间序列中存在不变的均值和时间趋势。单位根...
虚假相关 通过使用非参数检验来评估显着性,避免测量与幂律动态之间的虚假相关性...N Schaworonkow、DAJ Blythe、J Kegeles、G Curio、VV Nikulin:神经元和行为数据中的幂律动力学引入了虚假相关性。 人脑映射。 2015.
手册中包含了关于变频器编程语言的参考和操作指南。在详细介绍这些知识点之前,首先要指出的是,在阅读和使用变频器或任何工业自动化设备时,安全总是最重要的考虑因素。文档中不断重复提到“ATTENTION”和“重要”...
Missing_hole 、Mouse_bite、Open_circuit、Short、Spur、Spurious_copper六个文件夹,每个文件夹下分为images和labels两个目录,存放图片和对应的xml标签 除此之外,提供了可视化的脚本,展示后保存在当前目录下 ...
# Spurious Clojure AWS SDK Helper 受原始启发:它将 AWS 开发工具包配置为使用 (Spurious 是一个工具集,允许在本地针对 AWS 资源的子集进行开发)。 用法 您可以在标准 Clojure 应用程序中或通过在容器中运行的...