• 【转】有了互斥量,为什么还需要条件变量?


    一、互斥量和条件变量简介

      互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。在互斥量进行加锁以后,任何其它试图再次对互斥量加锁的线程将会阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥锁加锁,其它线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。

      条件变量(cond)是在多线程程序中用来实现“等待--》唤醒”逻辑的常用的方法。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”。条件变量的使用总是和一个互斥锁结合在一起。线程在改变条件状态前必须首先锁住互斥量,函数pthread_cond_wait把自己放到等待条件的线程列表上,然后对互斥锁解锁,这两个操作是原子操作(所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch(切换到另一个线程))。在函数返回时,互斥量再次被锁住。

    二、为什么存在条件变量

      首先,举个例子:在应用程序中有4个进程thread1,thread2,thread3和thread4,有一个int类型的全局变量iCount。iCount初始化为0,thread1和thread2的功能是对iCount的加1,thread3的功能是对iCount的值减1,而thread4的功能是当iCount的值大于等于100时,打印提示信息并重置iCount=0。

      如果使用互斥量,线程代码大概应是下面的样子:

    thread1/2:
           while (1)
           {
                 pthread_mutex_lock(&mutex);
                 iCount++;
                 pthread_mutex_unlock(&mutex);
           }
           thread4:
           while(1)
           {
                 pthead_mutex_lock(&mutex);
                 if (100 <= iCount)
                 {
                       printf("iCount >= 100
    ");
                       iCount = 0;
                       pthread_mutex_unlock(&mutex);
                 }
                 else
                 {
                       pthread_mutex_unlock(&mutex);
                 }
           }

      在上面代码中由于thread4并不知道什么时候iCount会大于等于100,所以就会一直在循环判断,但是每次判断都要加锁、解锁(即使本次并没有修改iCount)。这就带来了问题一:CPU浪费严重。所以在代码中添加了sleep(),这样让每次判断都休眠一定时间。但这又带来的第二个问题:如果sleep()的时间比较长,导致thread4处理不够及时,等iCount到了很大的值时才重置。对于上面的两个问题,可以使用条件变量来解决。

      首先看一下使用条件变量后,线程代码大概的样子:

    thread1/2:
           while(1)
           {
                   pthread_mutex_lock(&mutex);
                   iCount++;
                   pthread_mutex_unlock(&mutex);
                   if (iCount >= 100)
                   {
                          pthread_cond_signal(&cond);
                   }
           }         
           thread4:
           while (1)
           {
                  pthread_mutex_lock(&mutex);
                  while(iCount < 100)
                  {
                         pthread_cond_wait(&cond, &mutex);
                  }
                  printf("iCount >= 100
    ");
                  iCount = 0;
                  pthread_mutex_unlock(&mutex);
           }       

      从上面的代码可以看出thread4中,当iCount < 100时,会调用pthread_cond_wait。而pthread_cond_wait在上面应经讲到它会释放mutex,然后等待条件变为真返回。当返回时会再次锁住mutex。因为pthread_cond_wait会等待,从而不用一直的轮询,减少CPU的浪费。在thread1和thread2中的函数pthread_cond_signal会唤醒等待cond的线程(即thread4),这样当iCount一到大于等于100就会去唤醒thread4。从而不致出现iCount很大了,thread4才去处理。

      需要注意的一点是在thread4中使用的while (iCount < 100),而不是if (iCount < 100)。这是因为在pthread_cond_singal()和pthread_cond_wait()返回之间有时间差,假如在时间差内,thread3又将iCount减到了100以下了,那么thread4在pthread_cond_wait()返回之后,显然应该再检查一遍iCount的大小,这就是while的用意,如果是if,则会直接往下执行,不会再次判断。

      感觉可以总结为:条件变量用于某个线程需要在某种条件成立时才去保护它将要操作的临界区,这种情况从而避免了线程不断轮询检查该条件是否成立而降低效率的情况,这是实现了效率提高。在条件满足时,自动退出阻塞,再加锁进行操作。

      IBM上有个关于条件变量的文章:https://www.ibm.com/developerworks/cn/linux/thread/posix_thread3/ ,也可以看看。

     

    2017-10-09 15:23:43补充:

    1、

    问:条件变量为什么要与pthread_mutex一起使用呢?

    答:这是为了应对线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放(也就是 pthread_cod_wait() 释放锁并进入wait_cond状态 ,此时线程2上锁) 的时候才能调用cond_singal

    2、

    调用pthread_cond_signal后要立刻释放互斥锁(也可以将pthread_cond_signal放在pthread_mutex_lock和pthread_mutex_unlock之后),因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞

      转自:http://blog.csdn.net/bolike/article/details/9025389

  • 相关阅读:
    fetch的使用--当无法判断后台返回数据为什么类型时如何操作
    单页面与多页面间的区别及优缺点
    关于倒计时在关屏后不准确的问题
    前端分页仿百度分页效果
    pc端的弹性布局适配方案
    前端性能优化方向
    居民身份证号码组成规则
    axios简单介绍
    es6 promise 简单总结
    js原型链和原型链的继承
  • 原文地址:https://www.cnblogs.com/codingmengmeng/p/7249082.html
Copyright © 2020-2023  润新知