zoukankan      html  css  js  c++  java
  • 详解C++多线程(三)

    条件变量

    这一章主要讲讲条件变量condition_variable。条件变量是一个非常神奇的存在,是线程间交互的一种方式。

    C++11提供了condition_variable类。使用时需要include头文件<condition_variable>。

    如果把变量区看成是一座房子,那么前面两章频繁用到的mutex可以看成是房门的锁,正常来说是房门常年打开的,锁并用不上。但是有了多线程以后,为了防止多个线程一窝蜂胡乱篡改里面的数据,所以就有了锁的概念。

    现在假设每个线程都有一个管理锁的人,叫lock_guard,或者unique_lock,但是一次只能有一个人能够去操作锁(锁上或者是解锁)。一般来说他们是轮流去操作锁。而condition_variable则可以看做是门童,如果没有满足条件,门童就会通知线程的管锁人必须要休眠而不可以操作锁,可是一旦条件满足,他就会唤醒某些线程的管锁人可以去操作锁了。

    #include<thread>
    #include<iostream>
    #include<mutex>
    #include<string>
    #include<condition_variable>
    
    
    using namespace std;
    
    bool ready = false;
    bool processed = false;
    mutex mu;
    condition_variable cv;
    string data;
    
    void worker_thread()
    {
        unique_lock<mutex> locker(mu);
        
        //ready = false,此处相当于全局变量区的门童通知t线程休眠
        cv.wait(locker, [](){return ready;});
        
        //ready = true,休眠结束。此时locker上锁,开始修改变量
        cout<<"start processing data"<<endl;
        data += " after processing";
        processed = true;
        cout<<"worker thread has finished processing data"<<endl;
        
        
        //门童通知其他线程全局变量的最新情况
        //注意这一步是非常重要的,可以立即唤醒其他线程,否则其他线程会一直等待,这个过程可能会十分耗费时间
        //这也是为什么要用条件变量的原因
        cv.notify_one();
        
        //locker解锁
    }
    
    int main()
    {
        thread t(worker_thread); //启动t线程
        
        data = "example data";
        
        unique_lock<mutex> locker(mu);
        
        //locker开始上锁,main线程修改全局变量
        ready = true;
        cout<<"main signals data ready for processing"<<endl;
        
        
        //门童通知其他线程全局变量的最新情况
        cv.notify_one();
        
        //locker解锁
        //processed = false, 门童通知main线程休眠
        cv.wait(locker, [](){return processed;});
        
        //processed = true, locker上锁
        cout<<"back in main, data = "<<data<<endl;
        //locker解锁
        
        t.join();
    
    }

    上面的代码中需要注意一下几点:

    1. 代码中的 [](){return ready;}是匿名函数,也可以用循环的写法。

    //cv.wait(locker, [](){return ready;});
        
     //或者写成
    while(ready==false)
        cv.wait(locker);

    注意是while(ready == false),不是if(ready == false),因为wait的唤醒可能由于系统的原因被唤醒,这个的时机是不确定的。这个过程也被称作伪唤醒(spurious wakeup)。

    如果在错误的时候被唤醒,就开始执行了后面的操作就会造成错误。

    2. 注意cv.wait() 和cv.notify_all()或者cv.notify_one()需要搭配使用才能真正发挥条件变量的作用。

    3. cv.notify_one()指的是通知其中某一个线程,cv.notify_all()指的是通知全部线程。

    参考:

      https://www.jianshu.com/p/c1dfa1d40f53

  • 相关阅读:
    【今日CV 视觉论文速览】 19 Nov 2018
    【numpy求和】numpy.sum()求和
    【今日CV 视觉论文速览】16 Nov 2018
    【今日CV 视觉论文速览】15 Nov 2018
    poj 2454 Jersey Politics 随机化
    poj 3318 Matrix Multiplication 随机化算法
    hdu 3400 Line belt 三分法
    poj 3301 Texas Trip 三分法
    poj 2976 Dropping tests 0/1分数规划
    poj 3440 Coin Toss 概率问题
  • 原文地址:https://www.cnblogs.com/corineru/p/10850647.html
Copyright © 2011-2022 走看看