条件变量   
   
  条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。  
    
  1.   创建和注销   
    
  条件变量和互斥锁一样,都有静态动态两种创建方式,

  静态方式

  使用PTHREAD_COND_INITIALIZER常量,如下:     
      pthread_cond_t   cond=PTHREAD_COND_INITIALIZER     
    
  动态方式

  调用pthread_cond_init()函数,API定义如下:     
      int   pthread_cond_init(pthread_cond_t   *cond,   pthread_condattr_t   *cond_attr)    
    
  尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。   
    
  注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:    
  int   pthread_cond_destroy(pthread_cond_t   *cond)     
    
  2.   等待和激发   
    
    int   pthread_cond_wait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex)   
       int   pthread_cond_timedwait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex,   const   struct   timespec   *abstime)    
    
  等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。  
    
  无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race   Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。   
    
  激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

---------------------------------------------------------------

子线程中pthread_cond_wait()等待

pthread_mutex_t qlock;
pthread_cond_t  qready;
/************pthread_cond_wait()的使用方法**********/
pthread_mutex_lock(&qlock);    /*lock*/
//等待某资源,并以qready作为条件通知我们
pthread_cond_wait(&qready, &qlock); /*block-->unlock-->wait() return-->lock*/
//do something
pthread_mutex_unlock(&qlock); /*unlock*/

  

其他线程中pthread_cond_signal()唤醒

pthread_mutex_lock(&mtx);
//这个mutex_lock主要是用来保护wait等待临界资源,
//为何这里要有一个while (head == NULL)呢?
//因为如果有很多线程同时等待某资源,pthread_cond_wait里的线程可能会被意外唤醒,
//那么这个时候仍然head == NULL,这就是“惊群效应”
//这个时候,应该让线程继续进入pthread_cond_wait
while (head == NULL)  pthread_cond_wait(&cond, &mtx);
// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx
//然后阻塞在等待队列里休眠,直到再次被唤醒
//(大多数情况下是等待的条件成立而被唤醒)
//唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx); 再读取资源
// 用这个流程是比较清楚的/*block-->unlock-->wait() return-->lock*/
pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁

解析:

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,

关于这点的详细说明,可以参考这篇博客:http://blog.chinaunix.net/uid-11572501-id-3456343.html

用法详解
pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
用法:
pthread_cond_wait必须放在pthread_mutex_lock和pthread_mutex_unlock之间,因为他要根据共享变量的状态来决定是否要等待,而为了不永远等待下去所以必须要在lock/unlock队中。共享变量的状态改变必须遵守lock/unlock的规则。 pthread_cond_signal既可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有各缺点。
-----------------------------------------------------
之间:
pthread_mutex_lock
xxxxxxx
pthread_cond_signal
pthread_mutex_unlock

 缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的行为),所以一来一回会有性能的问题,造成低效。

  我们假设系统中有线程1和线程2,他们都想获取mutex后处理共享数据,再释放mutex。请看这种序列:
         1)线程1获取mutex,在进行数据处理的时候,线程2也想获取mutex,但是此时被线程1所占用,线程2进入休眠,等待mutex被释放。
         2)线程1做完数据处理后,调用pthread_cond_signal()唤醒等待队列中某个线程,在本例中也就是线程2。线程1在调用pthread_mutex_unlock()前,因为系统调度的原因,线程2获取使用CPU的权利,那么它就想要开始处理数据,但是在开始处理之前,mutex必须被获取,很遗憾,线程1正在使用mutex,所以线程2被迫再次进入休眠。
         3)然后就是线程1执行pthread_mutex_unlock()后,线程2方能被再次唤醒。
   从这里看,使用的效率是比较低的,如果再多线程环境中,这种情况频繁发生的话,是一件比较痛苦的事情。

  但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。推荐使用何种模式。

  posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是说,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。

-------------------------------------------------------
之后:
pthread_mutex_lock
xxxxxxx
pthread_mutex_unlock
pthread_cond_signal

  优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了

缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。
 所以,在Linux下最好pthread_cond_signal放中间,但从编程规则上说,其他两种都可以。

看段代码,消化一下:

#include <pthread.h>
#include <unistd.h>static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;struct node {
int n_number;
struct node *n_next;
} *head = NULL;/*[thread_func]*/
static void cleanup_handler(void *arg)
{printf("Cleanup handler of second thread./n");free(arg);(void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{struct node *p = NULL;pthread_cleanup_push(cleanup_handler, p);while (1) {pthread_mutex_lock(&mtx);           //这个mutex主要是用来保证pthread_cond_wait的并发性while (head == NULL)   {               //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。这个时候,应该让线程继续进入pthread_cond_waitpthread_cond_wait(&cond, &mtx);         // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源//用这个流程是比较清楚的/*block-->unlock-->wait() return-->lock*/}p = head;head = head->n_next;printf("Got %d from front of queue/n", p->n_number);free(p);pthread_mutex_unlock(&mtx);             //临界区数据操作完毕,释放互斥锁}pthread_cleanup_pop(0);return 0;
}int main(void)
{pthread_t tid;int i;struct node *p;pthread_create(&tid, NULL, thread_func, NULL);   //子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大/*[tx6-main]*/for (i = 0; i < 10; i++) {p = malloc(sizeof(struct node));p->n_number = i;pthread_mutex_lock(&mtx);             //需要操作head这个临界资源,先加锁,p->n_next = head;head = p;pthread_cond_signal(&cond);pthread_mutex_unlock(&mtx);           //解锁sleep(1);}printf("thread 1 wanna end the line.So cancel thread 2./n");pthread_cancel(tid);             //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。//关于取消点的信息:https://www.cnblogs.com/cthon/p/9078042.htmlpthread_join(tid, NULL);printf("All done -- exiting/n");return 0;
}

  

  

这篇博客对于条件变量的使用讲的也比较详细,大家可以参考一下:https://blog.csdn.net/Joogle/article/details/8010245

转载于:https://www.cnblogs.com/cthon/p/9084735.html

条件变量pthread_cond_wait()和pthread_cond_signal()详解相关推荐

  1. python中的class怎么用_对python 中class与变量的使用方法详解

    python中的变量定义是很灵活的,很容易搞混淆,特别是对于class的变量的定义,如何定义使用类里的变量是我们维护代码和保证代码稳定性的关键. #!/usr/bin/python #encoding ...

  2. python中class变量_对python 中class与变量的使用方法详解

    python中的变量定义是很灵活的,很容易搞混淆,特别是对于class的变量的定义,如何定义使用类里的变量是我们维护代码和保证代码稳定性的关键. #!/usr/bin/python #encoding ...

  3. Java中的宏变量,宏替换详解。

    转载自 Java中的宏变量,宏替换详解. 群友在微信群讨论的一个话题,有点意思,特拿出来分享一下. 输出true false 来看下面这段程序,和群友分享的大致一样. public static vo ...

  4. python定义静态变量_对Pyhon实现静态变量全局变量的方法详解

    python不能像C++一样直接定义一个static变量或者通过extern来导入别的库的变量而实现数据共享,但是python的思想是通过模块化来解决这个问题,就是通过模块来实现全局变量. 首先新建一 ...

  5. R语言 | xlsx包的安装(附链接:java环境变量的配置与详解)

    在下载xlsx包之前,要先下载java并且完成环境变量的设置 详细的教程见:(7条消息) java环境变量 的配置与详解(全网最详细教程)_S-D-C-L-Yourn的博客-CSDN博客_java环境 ...

  6. pthread_cond_wait()函数的详解

    http://hi.baidu.com/tjuer/item/253cc6d66b921317d90e4483 了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线 ...

  7. 条件变量 pthread_cond_wait

    1.先了解一下等待队列.(默认大家了解mutex,如果不了解:https://blog.csdn.net/qq_33890670/article/details/79967231) 等待队列,是指li ...

  8. 多线程条件变量(pthread_cond_wait)用法

    条件变量是利用线程间共享得全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起:另一个线程使"条件成立"给出条件成立信号.为了 ...

  9. Linux 条件变量 pthread_cond_wait

    条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起:另一个线程使"条件成立"(给出条件成立信号). ...

最新文章

  1. SpringBoot 概念和起步
  2. 一个通用的任务管理模型-golang
  3. Android开发学习笔记(二)——编译和运行原理(2)
  4. Spark入门实战系列--8.Spark MLlib(上)--机器学习及SparkMLlib简介
  5. 模板:二维线段树(线段树套线段树)
  6. 在图像中截取小图并保存
  7. Docker启动一个Centos镜像
  8. 花了 4 天,破解 UNIX 联合创始人 39 年前的密码!
  9. 写篇技术博客被简书锁定是一种什么样的体验
  10. 推荐两个非常不错的公众号
  11. 基于matlab的CIC滤波器仿真
  12. Linux网络管理实战2-组建局域网
  13. 国内学术科研论坛整理
  14. 还在用PPT做组织架构图?公司都在用的架构图软件是什么?
  15. Android强制竖屏
  16. 面试可能遇到java基础知识
  17. 小米电视es65、ea65、ex65和ec65区别
  18. Dos界面telnet命令的基本使用
  19. 图像分割中阈值的自动选取的研究及其算法实现
  20. 使用mvn命令时出现Project ‘xx‘ is duplicated in the reactor

热门文章

  1. Tomaso A.Poggio教授丨人工智能的下一个突破点在何处?
  2. CICC科普栏目丨时间之箭:从熵到大爆炸再到万物理论(一)
  3. 芯片的未来,靠这些技术了
  4. Gartner 2019 年 BI 炒作周期五大趋势:增强分析、数字文化、关系分析、决策智能、实施和扩展...
  5. 行业观察 | 新一轮AI周期里,华为拿什么破解核心难题?
  6. 潘云鹤院士:AI2.0和工业经济发展智能化
  7. 徐铁:当深度学习握手脑科学-圣城会议归来
  8. 腾讯帝国的野蛮生长史
  9. 自动驾驶真的会来得那么快吗:关于自动驾驶的7个疑问
  10. 你写代码,难道是因为热爱吗?