• 条件变量(二)


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

    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来进行条件判断,以保证临界区内只有一个线程在处理。 

  • 相关阅读:
    css圆,背景,img填满等样式
    MySQL双日志
    MySQL分层和查询数据的流程
    ZJNU 2345
    ZJNU 2342
    ZJNU 2340/2341/2343
    ZJNU 2235
    ZJNU 2226
    ZJNU 2212
    ZJNU 2208
  • 原文地址:https://www.cnblogs.com/howo/p/8577345.html
Copyright © 2020-2023  润新知