详解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

猜你喜欢

转载自www.cnblogs.com/corineru/p/10850647.html