C++20并发编程之线程闩(std::latch)和线程卡(std::barrier)

std::latch

std::latch类是一种基于std::ptrdiff_t类型的倒计数器,可用于同步线程。计数器的值在创建时进行初始化。线程可以在 latch 上阻塞,直到计数器减少到零为止。无法增加或重置计数器,这使得 latch 成为一次性的屏障。
std::latch的成员函数的并发调用(除了析构函数)不会引入数据竞争。
与std::barrier不同,std::latch可以被参与的线程多次递减。

主要成员函数

count_down: 这是一个非阻塞的成员函数,用于递减内部计数器。当一个线程完成了它在同步点的工作时,可以调用此函数来告诉std::barrier,一个线程已经到达。这个操作不会阻塞调用线程,因此线程可以继续执行其他任务。

try_wait: 这个成员函数用于测试内部计数器是否已经达到零。如果计数器为零,表示所有线程都已经到达同步点,可以执行后续操作。这是一个非阻塞操作,因为它只是测试计数器的状态而不阻塞调用线程。

wait: 这是一个阻塞的成员函数,用于使调用线程等待,直到内部计数器达到零。当所有线程都已经到达同步点,调用wait的线程将被解除阻塞,可以继续执行后续操作。

arrive_and_wait: 这是一个组合操作,它先递减计数器,然后等待直到计数器达到零。调用线程会递减计数器,然后被阻塞,直到所有线程都到达同步点。这是一个常见的用法,用于确保所有线程在继续执行之前都已经完成了特定的工作。

代码示例

#include <functional>
#include <iostream>
#include <latch>
#include <string>
#include <thread>struct Job
{const std::string name;std::string product{"not worked"};std::thread action{};
};int main()
{Job jobs[]{{"Annika"}, {"Buru"}, {"Chuck"}};std::latch work_done{std::size(jobs)};std::latch start_clean_up{1};auto work = [&](Job& my_job){my_job.product = my_job.name + " worked";work_done.count_down();start_clean_up.wait();my_job.product = my_job.name + " cleaned";};std::cout << "Work is starting... ";for (auto& job : jobs)job.action = std::thread{work, std::ref(job)};work_done.wait();std::cout << "done:\n";for (auto const& job : jobs)std::cout << "  " << job.product << '\n';std::cout << "Workers are cleaning up... ";start_clean_up.count_down();for (auto& job : jobs)job.action.join();std::cout << "done:\n";for (auto const& job : jobs)std::cout << "  " << job.product << '\n';
}

std::latch 的特点:

一次性的:std::latch 是一次性的,一旦计数器减至零,无法重新使用。如果需要可以多次使用的机制,可以考虑 std::barrier。

线程安全:std::latch 是线程安全的,多个线程可以同时调用 count_down 和 wait。

无超时等待:std::latch 的 wait 函数没有提供超时参数,如果需要超时等待,可以使用 std::barrier 或其他机制。

std::barrier

std::barrier类模板提供了一种线程协调机制,它阻塞了一个已知大小的线程组,直到该组中的所有线程都到达屏障为止。与std::latch不同,屏障是可重用的:一旦一组到达的线程被解除阻塞,就可以重新使用该屏障。与std::latch不同,屏障在解除线程阻塞之前执行一个可能为空的可调用对象。

屏障对象的生命周期由一个或多个阶段组成。每个阶段定义了一个阶段同步点,在该点等待的线程将被阻塞。线程可以到达屏障,但通过调用arrive可以延迟在阶段同步点上等待。这样的线程稍后可以通过调用wait在阶段同步点上阻塞。

主要成员函数

arrive: 这是 std::barrier 的一个成员函数,用于将线程到达栅栏并减少预期计数。当线程调用 arrive 时,预期计数会减少。一旦所有线程都到达这个栅栏,预期计数归零,栅栏会打开,允许所有线程继续执行。

wait: 这个成员函数使线程在阶段同步点阻塞,直到当前阶段的完成步骤运行。一旦线程调用 wait 并在同步点阻塞,它将一直等待直到当前阶段完成,然后才能继续执行。

arrive_and_wait: 这个成员函数结合了前两者的功能。它使线程到达栅栏并将预期计数减少一,然后在同步点阻塞,直到当前阶段完成。与简单的 arrive 不同,arrive_and_wait 在到达后会立即阻塞,直到当前阶段完成为止。

arrive_and_drop: 这个成员函数不仅减少当前阶段的预期计数,还会减少后续阶段的初始预期计数。这意味着它不仅影响当前阶段,还会影响未来阶段。这对于在某些情况下提前放弃等待是有用的,因为它不仅影响当前阶段的计数,还影响到后续的阶段。

代码示例

#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>int main()
{const auto workers = {"Anil", "Busara", "Carl"};auto on_completion = []() noexcept{// locking not needed herestatic auto phase ="... done\n""Cleaning up...\n";std::cout << phase;phase = "... done\n";};std::barrier sync_point(std::ssize(workers), on_completion);auto work = [&](std::string name){std::string product = "  " + name + " worked\n";std::osyncstream(std::cout) << product;  // ok, op<< call is atomicsync_point.arrive_and_wait();product = "  " + name + " cleaned\n";std::osyncstream(std::cout) << product;sync_point.arrive_and_wait();};std::cout << "Starting...\n";std::vector<std::jthread> threads;threads.reserve(std::size(workers));for (auto const& worker : workers)threads.emplace_back(work, worker);
}

主要特点

线程同步: 提供了一种机制,可以阻塞一组线程,直到所有线程都达到了某个同步点。这有助于协调并发执行的线程,确保它们在特定点同步执行或释放。

可重用: 与 std::latch 不同,std::barrier 是可重用的。一旦一组线程被释放,栅栏可以被重新使用,允许线程再次聚集在同一点。

阶段性同步: std::barrier 可以分为多个阶段,每个阶段定义了一个同步点。线程可以在同步点前到达,并在需要时等待。这使得可以在多个同步点执行特定的操作。

可定制的同步操作: 提供了一个回调函数,被称为 CompletionFunction,可以在所有线程到达同步点后执行。这个函数可以用来执行一些特定的操作,例如修改共享数据结构或执行其他同步操作。

多种成员函数: std::barrier 提供了不同的成员函数,如 arrive、wait、arrive_and_wait 和 arrive_and_drop,使得线程可以以不同的方式到达同步点并执行不同的同步操作。

线程安全: 除了析构函数外,std::barrier 的成员函数的并发调用不会引入数据竞争。这意味着多个线程可以安全地调用 std::barrier 的成员函数,而不需要额外的同步措施。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/146834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS原型对象prototype

让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…

2023NOIP游寄

停课停了一个月&#xff0c;考炸了就真的寄了。 DAY -2 模拟赛出人意外的简单&#xff0c;信心赛吗&#xff1f; 开局30s切了T1。总共做出三题&#xff0c;但挂了 150pts。难绷。 直接没有信心了。 DAY -1 晚上直接跑路回家&#xff0c;表示&#xff1a;休息一天。 DAY …

深度学习(五)softmax 回归之:分类算法介绍,如何加载 Fashion-MINIST 数据集

Softmax 回归 基本原理 回归和分类&#xff0c;是两种深度学习常用方法。回归是对连续的预测&#xff08;比如我预测根据过去开奖列表下次双色球号&#xff09;&#xff0c;分类是预测离散的类别&#xff08;手写语音识别&#xff0c;图片识别&#xff09;。 现在我们已经对回…

3DEXPERIENCE许可合规性:确保企业设计流程的合法与安全

在当今复杂多变的设计领域&#xff0c;企业需要格外关注设计流程的合规性。3DEXPERIENCE作为一款全球领先的设计软件&#xff0c;其许可合规性是确保企业设计流程合法与安全的重要保障。本文将详细介绍3DEXPERIENCE在许可合规性方面的优势和实践&#xff0c;帮助您更好地了解和…

Java多线程下使用TransactionTemplate控制事务

简介 本文展示了在Java的多线程环境下使用Spring的TransactionTemplate控制事务的提交与回滚&#xff0c;当任何一个子线程出现异常时&#xff0c;所有子线程都将回滚 环境 JDK&#xff1a;1.8.0_211 SpringBoot&#xff1a;2.5.10 说明 本文通过同时写入用户(User)和用户详细…

zookeperkafka学习

1、why kafka 优点 缺点kafka 吞吐量高&#xff0c;对批处理和异步处理做了大量的设计&#xff0c;因此Kafka可以得到非常高的性能。延迟也会高&#xff0c;不适合电商场景。RabbitMQ 如果有大量消息堆积在队列中&#xff0c;性能会急剧下降每秒处理几万到几十万的消息。如果…

记录一些涉及到界的题

文章目录 coppersmith的一些相关知识题1 [N1CTF 2023] e2Wrmup题2 [ACTF 2023] midRSA题3 [qsnctf 2023]浅记一下 coppersmith的一些相关知识 上界 X c e i l ( 1 2 ∗ N β 2 d − ϵ ) X ceil(\frac{1}{2} * N^{\frac{\beta^2}{d} - \epsilon}) Xceil(21​∗Ndβ2​−ϵ) …

elementPlus+vue3引入icon图标

安装包管理&#xff0c;推荐使用 yarn npm包有时候会有包冲突&#xff0c;在项目的根目录下执行&#xff0c;在终端下 # Yarn $ yarn add element-plus/icons-vue在main.js或main.ts中进行全局注册&#xff0c;导入所有图标 import * as ElementPlusIconsVue from element-plu…

【Proteus仿真】【STM32单片机】防火防盗GSM智能家居设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用声光报警模块、LCD1602显示模块、DS18B20温度、烟雾传感器模块、按键模块、PCF8591 ADC模块、红外检测模块等。 主要功能&#xff1a; 系统运行…

Leetcode_49:字母异位词分组

题目描述&#xff1a; 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 就是将相同字母组合的单词放在一起 示例 1: 输入: strs ["eat", "tea&qu…

Mac电脑好用的窗口管理软件 Magnet 中文for mac

Magnet是一款用于Mac操作系统的窗口管理工具&#xff0c;它可以帮助您快速和方便地组织和管理应用程序窗口&#xff0c;以提高您的工作效率和多任务处理能力。 以下是Magnet的一些主要功能和特点&#xff1a; 窗口自动调整&#xff1a;Magnet允许您通过简单的拖放操作或使用快…

使用Golang调用摄像头

近年来&#xff0c;摄像头成为了我们生活中不可或缺的设备之一。从智能手机到安全监控系统&#xff0c;无处不在的摄像头给我们带来了便利和安全。在开发摄像头相关的应用程序时&#xff0c;选择一种高效和易用的编程语言是非常重要的。本文将介绍如何使用Golang调用摄像头并进…

同态加密定义,四大发展阶段总结,FHE系统正式定义-全同态加密

目录 同态加密定义 为什么采用电路模型? 四大发展阶段 总结 FHE系统正式定义

微信小程序 限制字数文本域框组件封装

微信小程序 限制字数文本域框 介绍&#xff1a;展示类组件 导入 在app.json或index.json中引入组件 "usingComponents": {"text-field":"/pages/components/text-field/index"}代码使用 <text-field maxlength"500" bindtabsIt…

IDEA创建文件添加作者及时间信息

前言 当使用IDEA进行软件开发时&#xff0c;经常需要在代码文件中添加作者和时间信息&#xff0c;以便更好地维护和管理代码。 但是如果每次都手动编辑 以及修改那就有点浪费时间了。 实践 其实我们可以将注释日期 作者 配置到 模板中 同时配置上动态获取内容 例如时间 这样…

PaddleClas学习2——使用PPLCNet模型对车辆朝向进行识别(python)

使用PPLCNet模型对车辆朝向进行识别 1. 配置PaddlePaddle,PaddleClas环境2. 准备数据2.1 标注数据格式2.2 标注数据3. 模型训练3.1 修改配置文件3.2 训练、评估4 模型预测1. 配置PaddlePaddle,PaddleClas环境 安装:请先参考文档 环境准备 配置 PaddleClas 运行环境。 2. 准…

美国服务器:全面剖析其主要优点与潜在缺点

​  服务器是网站搭建的灵魂。信息化的今天&#xff0c;我们仍需要它来为网站和应用程序提供稳定的运行环境。而美国作为全球信息技术靠前的国家之一&#xff0c;其服务器市场备受关注。那么&#xff0c;美国服务器究竟有哪些主要优点和潜在缺点呢? 优点 数据中心基础设施&a…

Vue 路由缓存 防止路由切换数据丢失

在切换路由的时候&#xff0c;如果写好了一丢数据在去切换路由在回到写好的数据的路由去将会丢失&#xff0c;这时可以使用路由缓存技术进行保存&#xff0c;这样两个界面来回换数据也不会丢失 在 < router-view >展示的内容都不会被销毁&#xff0c;路由来回切换数据也…

Linux上编译和安装SOFA23.06

前言 你可以直接使用编译安装好的SOFA版本Installing from all-included binaries (v23.06.00)&#xff1a; 如果你想自己编译&#xff0c;可以看我下面写的内容&#xff0c;不过绝大多数是从官网来的&#xff0c;如果和官网有出入&#xff0c;建议还是以官网为准。 在Linux下…

PVE Win平台虚拟机下如何安装恢复自定义备份Win系统镜像ISO文件(已成功实现)

环境: Virtual Environment 7.3-3 Win s2019 UltraISO9.7 USM6.0 NTLite_v2.1.1.7917 问题描述: PVE Win平台虚拟机下如何安装恢复自定义备份Win系统镜像ISO文件 本次目标 主要是对虚拟机里面Win系统备份做成可安装ISO文件恢复至别的虚拟机或者实体机上 解决方案: …