栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++多线程std::condition

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++多线程std::condition

0 引言

在上篇文章C++多线程 task--std::promise和std::future_qls315的博客-CSDN博客

介绍了std::promise和std::future,其可以模拟一次性事件通知机制(这也是条件变量的功能之一)

为了补充相应的事件通知机制相应的方式,本文继续介绍std::condition_variable(条件变量).

1 std::condition_variable(条件变量)

条件变量是一种C++标准提供的同步原语,其可以用来阻塞一个或者多个线程,直到某个线程修改了共享变量(condition),并且向条件变量发送通知。

执行修改共享变量的线程,必须执行如下过程

  • 获得std::mutex,可通过std::lock_guard管理
  • 修改共享变量
  • 执行条件变量的notify_one或者notify_all方法

具体来说,条件变量的工作原理可参考下图

 2 条件变量相应接口介绍

 一个条件变量cv可以看作一个生产者也可以看作一个消费者,其提供的接口如下所示

cv.notify_one()    // 通知一个正在等待的线程

cv.notify_all()    // 通知所有正在等待的线程

cv.wait(lock, ...)  // 等待一个通知,lock一般为std::unique_lock

cv.wait_for(lock, relTime, ...) // 等待一个通知,如果等待的间隔到relTime时,仍未等到通知,线程被唤醒

// lock为std::unique_lock

cv.wait_until(lock, absTime, ...) // 等待一个通知,如果到absTime时间点,仍未等到通知,线程被唤醒, lock为std::unique_lock

cv.native_handle(). // 返回这个条件变量的底层实现的handle
3 条件变量的注意事项

使用条件变量时一般需要了解如下两个概念:Lost Wakeup 和 Spurious Wakeup。

Lost Wakeup :

所谓丢失唤醒,指的是在接受线程进入等待状态(也即加入wait queue)之前,生产者线程便发布了相应的通知(事件), 此时导致接受者线程陷入一直等待的状态。

Spurious Wakeup:

所谓虚假唤醒,指的是在没有收到相应的通知,线程也会被唤醒。

为了演示Lost wakeup, 可参考如下代码

#include 
#include 
#include 
#include 

std::mutex mut;
std::condition_variable cv;

void consume() {
  std::cout << "Consume thread: wait notifyn";
  std::unique_lock lck(mut);
  cv.wait(lck);

  std::cout << "Consume thread: receve notifyn";
}

void produce() {
  std::cout << "Produce thread: send notifyn";
  cv.notify_one();
}

int main() {
  std::thread t1(produce);
  std::thread t2(consume);
  t1.join();
  t2.join();
  return 0;
}

上述输出结果如下所示,也即通知事件先发布,后进入wait queue。

 4 代码示例

由上述第3部分可知,条件变量的wait接口必须要使用谓词,防止出现lost wakeup。

通过《C++ Concurrency in Action》可知,条件变量的wait接口一般是使用忙等待实现的,并对其进行了部分优化,简言之,其实现如下

template
void minimal_wait(std::unique_lock& lk,Predicate pred){
    while(!pred()){
        lk.unlock();
        lk.lock(); }
}

为了方便理解,可以将条件变量的wait接口按照如下方式进行理解

cv.wait(lck, pred)

==>

std::unique_lock lck(mut)
while (!pred) {
  cv.wait(lck);
}

本部分的代码参考于《C++ Concurrency in Action》 第2版,并做了相应修改

#include 
#include 
#include 
#include 

std::mutex mut;
bool data_ready{false};
std::condition_variable cv;

void data_preparation_thread() {
  std::cout << "data readyn";
  {
    std::lock_guard lg(mut);
    data_ready = true;
  }
  
  cv.notify_one();
}

void data_processing_thread() {
  std::cout << "data_processing_thread wait notifyn";
  std::unique_lock lck(mut);
  cv.wait(lck, []() { return data_ready; });
  std::cout << "data_processing_thread: receve notifyn";
}

int main() {
  std::thread t1(data_preparation_thread);
  std::thread t2(data_processing_thread);
  t1.join();
  t2.join();
  return 0;
}

其输出结果可自行验证。

5 总结

本文总结了现代C++种多线程中使用的条件变量的相关介绍及可能出现的问题。通过本文应该可以初步使用条件变量。

参考:std::condition_variable - cppreference.com

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1000963.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号