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

  • 相关阅读:
    vue $refs的静态绑定使用与动态绑定使用
    net core中Vue.component单独一个文件不运行,不报错的处理
    C语言之指针基础
    C语言之指针函数
    指针强化
    C语言之指针数组
    C语言之数组
    C语言之数据类型
    C语言之内存管理
    C语言之流程控制
  • 原文地址:https://www.cnblogs.com/corineru/p/10850647.html
Copyright © 2011-2022 走看看