Modern C++多线程同步: 由简至繁的三个案例详解

您所在的位置:网站首页 vivox6a什么时候生产的 Modern C++多线程同步: 由简至繁的三个案例详解

Modern C++多线程同步: 由简至繁的三个案例详解

2023-02-21 06:14| 来源: 网络整理| 查看: 265

最近又回顾了一下C++多线程看到一篇文章很不错, 翻译一下并且添加一些我自己的见解. 文章来源: https://chrizog.com/cpp-thread-synchronization

多线程的两个主要用途保护共享数据(Protecting shared data from concurrent access)同步并行操作(Synchronizing concurrent operations)保护共享数据(Protecting shared data from concurrent access)

一般我们用互斥锁(mutual exclusion, mutex)来保护共享数据. 比如在一些场景下, 有一些线程往共享数据里写数据, 有一些线程从共享数据里读数据. 两个或多个线程读数据完全没问题, 但是我们要防止两个线程同时写数据, 也要防止一个线程在写的同时另一个线程还在读. 否则这样就会发生竞争条件(race condition, 官方翻译是"竞争条件", 但我觉得可以直译成"竞争情况"或者意译成"数据竞争").

同步并行操作(Synchronizing concurrent operations)

这篇文章我们主要关注同步并行操作. 同步并行的意思是有可能我们会同时运行不同的事件(event), 我们需要协调他们的发生顺序, 比如一个线程必须要等另一个线程完成它的工作才能进行这个线程本身的下一部分工作.

大部分这样类型的问题都可以总结成生产者和消费者模式(producer-consumer pattern): 消费者需要等待生产者生产数据, 并且等待某种条件成真. 生产者生产数据并且改变那种条件.

同步并行操作的三种方法

在同步并行操作, 也就是在生产者和消费者模式里, 让消费者等待生产者设定的某个条件, 可以用三种方法完成:

周期性检查条件(最简单但是最差的实现方法)使用条件变量(condition variables)使用futures和promises(理解起来比较复杂, 但有些场景可以很方便)方法1: 周期性检查条件

这是一个最简单但是最不好的方法, 看看它的优缺点:

优点: 容易理解

缺点:

消耗大量资源周期性检查(CPU使用率可能会飙升到100%)不必要地周期性唤醒线程垃圾设计

我们从一个简单的生产者消费者案例说起. 假如一个场景我们有一个送快递的线程(生产者), 和一个签收的线程(消费者). 消费者等待生产者的那个"条件", 就是快递是否准备好被接收了.

代码框架可以这样:

struct Packet { int id; }; std::mutex m; std::queue packet_queue; //生产者 - 生成快递包裹 void parcel_delivery() {} //消费者 - 签收包裹 void recipient() {} int main() { // 启动生产者和消费者线程 }

我们来看看最原始的用互斥锁(mutex)的代码实现怎么写:

#include #include #include #include #include #include struct Packet { int id; }; std::mutex m; std::queue packet_queue; //生产者 - 生成快递包裹 void parcel_delivery() { // 周期性地生产"包裹" int packet_id = 0; while (true) { // 等待一段时间再投送 std::this_thread::sleep_for(std::chrono::seconds(1)); Packet new_packet{packet_id}; { //使用lock_guard锁住这段括号{}以内的共享数据, 走出{}段落后会自动解锁 //为什么用lock_guard? 这段代码即使出错, throw了, 这个段落结束后也会解锁, //从而避免死锁(Dead lock) std::lock_guard l{m}; std::cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3