zoukankan      html  css  js  c++  java
  • 条件变量(二)

        条件变量是用来等待某个条件是否成立的等待原语。主要有两个函数:

    1、 pthread_cond_signal(&cond) :用来发送信号,唤醒正在wait()ing的线程。要搞清楚这里所谓唤醒,要知道其实线程中有两个队列cond_wait和mutex_lock。signal的唤醒只是将cond_wait中的一个线程放入mutex_lock中,而不是回到正常工作状态(所以性能损耗可忽略)。代码中一般在调用signal后,紧接着会解锁,此时mutex_lock队列中的阻塞线程获得互斥锁,上锁。

    2、 pthread_cond_wait() :用来等待信号。一般(个人觉得是必须)用while对某个条件进行判断,条件不满足时进入调用pthread_cond_wait()函数。调用wait时先后做了这几个动作:

         解锁-->挂起等待(进入cond_wait,等待signal唤醒)-->被signal唤醒,进入(cond_mutex队列)--重新加锁-

    然后进入下一次while循环,判断条件是否还满足。

    循环判断的原因如下:两个线程同时调用signal,唤醒两个cond_wait队列中的线程,但是他们存在race condition,两个线程要竞争同一个互斥锁:比如线程1、2竞争,但是线程1竞争到了互斥锁,那么线程2有阻塞了;虽然已经从cond_wait中出来了(并不是从pthread_cond_wait返回);等到线程1释放锁,线程2持有了锁,此时必须循环再检查条件,因为条件已经被线程2修改了。这就是为什么要用while而不是if的原因。避免wait从队列中出来、条件被别的线程修改了。如果是if就造成了虚假唤醒(suprious wake)。为避免这点,必须醒来后再次判断睡眠条件。

    下面是一段简单的多线程代码:

     1 pthread_mutex_t m=PTHREAD_MUTEX_INITIALIZER;
     2 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     3 bool signaled =false;
     4 
     5 void waiter()
     6 {
     7     pthread_mutex_lock(&m);
     8     while(!signaled)            //一定要是while,而不是if
     9         pthread_cond_wait(&cond, &m);
    10 
    11     dosomething(x,y,...);
    12     pthread_mutex_unlock(&m);
    13 }
    14 
    15 void signal()
    16 {
    17     pthread_mutex_lock(&m);
    18     signaled = true;            //先改变条件,再调用signal
    19     pthread_cond_signal(&cond);
    20     pthread_mutex_unlock(&m);
    21 }

    至于为什么在被唤醒之后还要再次进行条件判断(即为什么要使用while循环来判断条件),是因为可能有“惊群效应”。有人觉得此处既然是被唤醒的,肯定是满足条件了,其实不然。如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。

    至于先改变条件值,再调用pthread_cond_signal是因为如果先调用pthread_cond_signal唤醒pthread_cond_wait线程,而此时还没来得及改变signaled,此时wait线程再次while判断signaled的值,发现条件不满足,再次陷入pthread_cond_wait()。这就叫做事件丢失。我在github上问过陈硕大神https://github.com/chenshuo/recipes/issues/18,问他通知和修改是不是写反了,他说持有锁的情况下这么写是正确的,不会导致事件丢失。代码如下:

    1 void broadcast()
    2 {
    3     pthread_mutex_lock(&mutex_);
    4     pthread_cond_broadcast(&cond_);
    5     signaled_ = true;
    6     pthread_mutex_unlock(&mutex_);
    7 }

    从上文可以看出: 
    1,pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,while循环的意义就体现在这里了,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 
    2,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐此处使用while循环. 

           其实说白了很简单,就是pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话,就必须要使用while来进行条件判断,以保证临界区内只有一个线程在处理。 

  • 相关阅读:
    Step by step Dynamics CRM 2013安装
    SQL Server 2012 Managed Service Account
    Step by step SQL Server 2012的安装
    Step by step 活动目录中添加一个子域
    Step by step 如何创建一个新森林
    向活动目录中添加一个子域
    活动目录的信任关系
    RAID 概述
    DNS 正向查找与反向查找
    Microsoft Dynamics CRM 2013 and 2011 Update Rollups and Service Packs
  • 原文地址:https://www.cnblogs.com/howo/p/8577345.html
Copyright © 2011-2022 走看看