c/c++ 多线程 一个线程等待某种事件发生

多线程 一个线程等待某种事件发生

背景:某个线程在可以完成其任务以前可能须要等待另外一个线程完成其任务。

例如:坐夜间列车,为了可以不坐过站,

1,整夜保持清醒,可是这样你就会很是累,不可以睡觉。

2,若是你知道几点会到你要下车的站,就能够提早定个闹钟,而后睡觉等待闹钟叫醒你,可是若是车中间有延误,闹钟响了,可是还没到你要下次的站;还有一种更恶劣的状况就是,闹钟还没响,可是列车已通过站了。

3,最好的办法就是,快到站前,有我的能把你叫醒。

为了可以达到上面场景3的效果,条件变量(Condition variable)就登场了。

对应上面的3个场景,请看下面的代码。

场景1的代码:

while(某个条件){//这个条件由另外一个线程来变动,因此就一直循环来检查这个条件,CPU就得不到休息,浪费系统的性能
    
}

场景2的代码:

std::unique_lock<std::mutex> lk(m);
while(某个条件){//这个条件由另外一个线程来变动,先睡眠一会,等待别的线程变动这个条件,CPU获得了休息,节省了系统的性能
    lk.unlock();
    sleep(休眠必定的时间);
    lk.lock();
}
//缺点:没法准确知道要休眠多长的时间。休眠时间过长就会致使响应过慢,休眠时间太短,醒来发现条件还没被变动,还得继续休眠。

场景3的代码:

#include <iostream>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <thread>
#include <unistd.h>//sleep

std::mutex mut;
std::queue<int> data_queue;//-------------------①
std::condition_variable data_cond;

void data_preparation_thread(){
  int data = 0;
  while(true){
    data++;
    std::lock_guard<std::mutex> lk(mut);
    data_queue.push(data);//-------------------②
    data_cond.notify_one();//-------------------③
    std::cout << "after  notify_one" << std::endl; 
    //std::this_thread::sleep_for(1000);
    sleep(1);
  }
}

void data_process_thread(){
  while(true){
    std::unique_lock<std::mutex> lk(mut);//-------------------④
    std::cout << "before wait" << std::endl; 
    data_cond.wait(lk, []{return !data_queue.empty();});//-------------------⑤
    std::cout << "after  wait" << std::endl;
    int data = data_queue.front();
    std::cout << data << std::endl;
    data_queue.pop();
    lk.unlock();//-------------------⑥
    //假设处理数据data的函数process要花费大量时间,因此提早解锁
    //process(data);
  }
}
int main(){
  std::thread t1(data_preparation_thread);
  std::thread t2(data_process_thread);
  t1.join();
  t2.join();
}

1,有一个在多个线程间传递数据的队列①,修改队列前锁定队列,把数据压入队列②,压入完成后通知等待它的线程,说:我已经把数据作好,大家可使用了③。

2,另外一个线程使用队列前,先锁定这个队列④,注意是用std::unique_lock而不是std::lock_guard,理由后面说。

3,data_cond.wait(),检查队列里是否有数据(用的是lambda函数,也能够是普通函数),

  • 若是条件不知足(lambda函数返回false),wait解锁这个互斥元,并将该线程置于阻塞状态,继续等待notify_onde()来唤醒它。
  • 若是条件知足(lambda函数返回true),wait继续锁定这个互斥元,执行wait后面的代码。

这就是为何使用std::unique_lock而不是std::lock_guard。等待中的线程必须解锁互斥元,并在wait返回true的时候从新锁定这个互斥元,std::lock_guard没有这个功能。若是线程在等待期间不解锁互斥元,把数据压入队列的线程就没法锁定这个互斥元,就没法压入数据,就没法执行notify_one(),因此等待的线程就永远处于等待状态。。。ios

4,std::unique_lock另外的灵活性,假设获得队列里的数据后,要作一个特别耗时的处理,作这个耗时的处理前就应该解锁这个互斥元⑥,std::unique_lock提供了这个灵活性,而std::lock_guard没有提供这个灵活性。

5,notify_one()后,另外一个wait的线程不是立刻就被唤醒!!!

github源代码c++

编译方法:git

g++ -g condition_vari-4.1.cpp -std=c++11  -pthread

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

相关文章
相关标签/搜索