当前位置: 首页 > news >正文

标准库标头 <barrier>(C++20)学习

此头文件是线程支持库的一部分。

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

屏障对象的生存期由一个或多个屏障阶段组成。每个阶段定义一个阻塞线程的阶段同步点。线程可以抵达屏障,但通过调用 arrive 来推迟它在阶段同步点上的等待。这样的线程可以随后再通过调用 wait 在阶段同步点上阻塞。

屏障 阶段 由以下步骤组成:

  1. 每次调用 arrive 或 arrive_and_drop 减少期待计数
  2. 期待计数抵达零时,运行阶段完成步骤,即调用 completion,并解除所有在阶段同步点上阻塞的线程。完成步骤的结束强先发生于所有从完成步骤所除阻的调用的返回。
    在期待计数抵达零后,一个线程会在其调用 arrive、arrive_and_drop 或 wait 的过程中执行完成步骤恰好一次,但如果没有线程调用 wait 则是否执行完成步骤为实现定义。
  3. 完成步骤结束时,重置期待计数为构造中指定的值,它可能为 arrive_and_drop 调用所调整,自此开始下一阶段。

并发调用barrier 除了析构函数外的成员函数不会引起数据竞争。

vs2022 类模板如下:

class barrier {
public:static_assert(
#ifndef __cpp_noexcept_function_typeis_function_v<remove_pointer_t<_Completion_function>> ||
#endif // !defined(__cpp_noexcept_function_type)is_nothrow_invocable_v<_Completion_function&>,"N4950 [thread.barrier.class]/5: is_nothrow_invocable_v<CompletionFunction&> shall be true");using arrival_token = _Arrival_token<_Completion_function>;constexpr explicit barrier(const ptrdiff_t _Expected, _Completion_function _Fn = _Completion_function()) noexcept /* strengthened */: _Val(_One_then_variadic_args_t{}, _STD move(_Fn), _Expected << _Barrier_value_shift) {_STL_VERIFY(_Expected >= 0 && _Expected <= (max) (),"Precondition: expected >= 0 and expected <= max() (N4950 [thread.barrier.class]/9)");}barrier(const barrier&)            = delete;barrier& operator=(const barrier&) = delete;_NODISCARD static constexpr ptrdiff_t(max)() noexcept {return _Barrier_max;}_NODISCARD_BARRIER_TOKEN arrival_token arrive(ptrdiff_t _Update = 1) noexcept /* strengthened */ {_STL_VERIFY(_Update > 0 && _Update <= (max) (), "Precondition: update > 0 (N4950 [thread.barrier.class]/12)");_Update <<= _Barrier_value_shift;// TRANSITION, GH-1133: should be memory_order_releaseptrdiff_t _Current = _Val._Myval2._Current.fetch_sub(_Update) - _Update;_STL_VERIFY(_Current >= 0, "Precondition: update is less than or equal to the expected count ""for the current barrier phase (N4950 [thread.barrier.class]/12)");if ((_Current & _Barrier_value_mask) == 0) {// TRANSITION, GH-1133: should have this fence:// atomic_thread_fence(memory_order_acquire);_Completion(_Current);}// Embedding this into the token to provide an additional correctness check that the token is from the same// barrier and wasn't used. All bits of this fit, as barrier should be aligned to at least the size of an// atomic counter.return arrival_token{(_Current & _Barrier_arrival_token_mask) | reinterpret_cast<intptr_t>(this)};}void wait(arrival_token&& _Arrival) const noexcept /* strengthened */ {_STL_VERIFY((_Arrival._Value & _Barrier_value_mask) == reinterpret_cast<intptr_t>(this),"Preconditions: arrival is associated with the phase synchronization point for the current phase ""or the immediately preceding phase of the same barrier object (N4950 [thread.barrier.class]/19)");const ptrdiff_t _Arrival_value = _Arrival._Value & _Barrier_arrival_token_mask;_Arrival._Value                = _Barrier_invalid_token;for (;;) {// TRANSITION, GH-1133: should be memory_order_acquireconst ptrdiff_t _Current = _Val._Myval2._Current.load();_STL_VERIFY(_Current >= 0, "Invariant counter >= 0, possibly caused by preconditions violation ""(N4950 [thread.barrier.class]/12)");if ((_Current & _Barrier_arrival_token_mask) != _Arrival_value) {break;}_Val._Myval2._Current.wait(_Current, memory_order_relaxed);}}void arrive_and_wait() noexcept /* strengthened */ {// TRANSITION, GH-1133: should be memory_order_acq_relptrdiff_t _Current       = _Val._Myval2._Current.fetch_sub(_Barrier_value_step) - _Barrier_value_step;const ptrdiff_t _Arrival = _Current & _Barrier_arrival_token_mask;_STL_VERIFY(_Current >= 0, "Precondition: update is less than or equal to the expected count ""for the current barrier phase (N4950 [thread.barrier.class]/12)");if ((_Current & _Barrier_value_mask) == 0) {_Completion(_Current);return;}for (;;) {_Val._Myval2._Current.wait(_Current, memory_order_relaxed);// TRANSITION, GH-1133: should be memory_order_acquire_Current = _Val._Myval2._Current.load();_STL_VERIFY(_Current >= 0, "Invariant counter >= 0, possibly caused by preconditions violation ""(N4950 [thread.barrier.class]/12)");if ((_Current & _Barrier_arrival_token_mask) != _Arrival) {break;}}}void arrive_and_drop() noexcept /* strengthened */ {const ptrdiff_t _Rem_count =_Val._Myval2._Total.fetch_sub(_Barrier_value_step, memory_order_relaxed) - _Barrier_value_step;_STL_VERIFY(_Rem_count >= 0, "Precondition: The expected count for the current barrier phase ""is greater than zero (N4950 [thread.barrier.class]/24) ""(checked initial expected count, which is not less than the current)");(void) arrive(1);}private:void _Completion(const ptrdiff_t _Current) noexcept {const ptrdiff_t _Rem_count = _Val._Myval2._Total.load(memory_order_relaxed);_STL_VERIFY(_Rem_count >= 0, "Invariant: initial expected count less than zero, ""possibly caused by preconditions violation ""(N4950 [thread.barrier.class]/24)");_Val._Get_first()();const ptrdiff_t _New_phase_count = _Rem_count | ((_Current + 1) & _Barrier_arrival_token_mask);// TRANSITION, GH-1133: should be memory_order_release_Val._Myval2._Current.store(_New_phase_count);_Val._Myval2._Current.notify_all();}struct _Counter_t {constexpr explicit _Counter_t(ptrdiff_t _Initial) : _Current(_Initial), _Total(_Initial) {}// wait(arrival_token&&) accepts a token from the current phase or the immediately preceding phase; this means// we can track which phase is the current phase using 1 bit which alternates between each phase. For this// purpose we use the low order bit of _Current.atomic<ptrdiff_t> _Current;atomic<ptrdiff_t> _Total;};_Compressed_pair<_Completion_function, _Counter_t> _Val;
};

成员对象

名称定义
completion (私有)CompletionFunction 类型的完成函数对象,在每个阶段完成步骤调用。
(仅用于阐述的成员对象*)

成员类型

名称定义
arrival_token未指定的对象类型,满足可移动构造 (MoveConstructible) 、可移动赋值 (MoveAssignable) 及可析构 (Destructible) 

成员函数

(构造函数)

构造 barrier
(公开成员函数)

(析构函数)

销毁 barrier
(公开成员函数)

operator=

[弃置]

barrier 不可赋值
(公开成员函数)

arrive

到达屏障并减少期待计数
(公开成员函数)

wait

在阶段同步点阻塞,直至运行其阶段完成步骤
(公开成员函数)

arrive_and_wait

到达屏障并把期待计数减少一,然后阻塞直至当前阶段完成
(公开成员函数)

arrive_and_drop

将后继阶段的初始期待计数和当前阶段的期待计数均减少一
(公开成员函数)
常量

max

[静态]

实现所支持的期待计数的最大值
(公开静态成员函数)

示例代码:

#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>int main()
{const auto workers = { "Anil", "Busara", "Carl" };auto on_completion = []() noexcept{// 此处无需锁定static auto phase ="... 完成\n""清理...\n";std::cout << phase;phase = "... 完成\n";};std::barrier sync_point(std::ssize(workers), on_completion);auto work = [&](std::string name){std::string product = "  " + name + " 已工作\n";std::osyncstream(std::cout) << product;  // OK, op<< 的调用是原子的sync_point.arrive_and_wait();product = "  " + name + " 已清理\n";std::osyncstream(std::cout) << product;sync_point.arrive_and_wait();};std::cout << "启动...\n";std::vector<std::jthread> threads;threads.reserve(std::size(workers));for (auto const& worker : workers)threads.emplace_back(work, worker);return 0;
}

运行结果:

另一个示例:

#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>std::barrier bar(4); //创建一个barrier,需要4个线程到达同步点void thread_func(int id) {// 线程执行一些任务//std::cout << "Thread ID: " << std::this_thread::get_id() << " is doing some work." << std::endl;std::cout << "Thread ID=======start===== is doing some work.\n";// 等待所有线程到达栅栏bar.arrive_and_wait();// 所有线程到达栅栏后,继续执行后续任务//std::cout << "Thread ID: " << std::this_thread::get_id() << " continues working after barrier." << std::endl;std::cout << "Thread ID=======end: ===== continues working after barrier.\n";
}int main() {constexpr int num_threads = 4;std::vector<std::thread> threads;// 创建线程并执行线程函数for (int i = 0; i < num_threads; ++i) {threads.emplace_back(thread_func, i);}// 等待所有线程执行完毕for (auto& t : threads) {t.join();}return 0;
}

示例代码:

#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>std::barrier bar(4); //创建一个barrier,需要4个线程到达同步点void thread_func(int id) {// 线程执行一些任务//std::cout << "Thread ID: " << std::this_thread::get_id() << " is doing some work." << std::endl;std::cout << "Thread ID=======start===== is doing some work.\n";// 等待所有线程到达栅栏bar.arrive_and_wait();// 所有线程到达栅栏后,继续执行后续任务//std::cout << "Thread ID: " << std::this_thread::get_id() << " continues working after barrier." << std::endl;std::cout << "Thread ID=======end: ===== continues working after barrier.\n";
}int main() {constexpr int num_threads = 4;std::vector<std::thread> threads;// 创建线程并执行线程函数for (int i = 0; i < num_threads; ++i) {threads.emplace_back(thread_func, i);}// 等待所有线程执行完毕for (auto& t : threads) {t.join();}return 0;
}

运行结果:

参考:

std::barrier - cppreference.com

【C++ 20 并发工具 std::barrier】掌握并发编程:深入理解C++的std::barrier_c++ barrier-CSDN博客

相关文章:

标准库标头 <barrier>(C++20)学习

此头文件是线程支持库的一部分。 类模板 std::barrier 提供一种线程协调机制&#xff0c;阻塞已知大小的线程组直至该组中的所有线程到达该屏障。不同于 std::latch&#xff0c;屏障是可重用的&#xff1a;一旦到达的线程组被解除阻塞&#xff0c;即可重用同一屏障。与 std::l…...

如何测量一个(传输网络)系统的容量

Little 定律就能反算系统容量&#xff0c;但我这篇文章要正着算。 假想一个理发店场景。李大爷拥有一家占地 50 平米的理发店&#xff0c;经理到店里理发如果已经有经理在理发&#xff0c;就要拿个券等待&#xff0c;请问李大爷需要印多少等待券&#xff1f; 这是个系统容量问…...

【MySQL】MySQL和Workbench版本兼容问题

1、安装MySQL WorkBench 最新版本下载&#xff1a;https://dev.mysql.com/downloads/workbench/ 历史版本下载&#xff1a;https://downloads.mysql.com/archives/workbench/ 2、问题描述 本人在Windows下安装了一个旧版本的MySQL&#xff08;5.1&#xff09;&#xff0c;同…...

项目实战 ---- 商用落地视频搜索系统(10)---后台搜索Cache优化

目录 背景 技术实现策略 视频预处理阶段的cache技术 视频搜索阶段的cache技术 技术实现 预处理阶段cache策略实现 逻辑 代码 运行结果 问题及注意点 搜索阶段cache策略实现 系统配置层面 逻辑 低版本 GPU CPU 本项目的配置 高版本 描述 go ahead 策略 cac…...

客户端(服务器下载文件)

一、客户端代码 客户端代码 //实现TCP客户端通信 #include<stdio.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<sys/socket.h> #include<string.h> #include<netinet/ip.h> #include<netinet/in…...

P1544 三倍经验 (记忆化搜索)

三倍经验 题目描述 数字金字塔由 n n n 行整数组成&#xff0c;第 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n) 行有 i i i 个数字&#xff0c;一个示例如下。 73 98 1 02 7 4 4 4 5 2 6 5现在你在金字塔的顶部&#xff08;第一行&#xff09;&…...

【在Python中创建简单界面计算器】

在Python中创建带有简单界面的计算器&#xff0c;我们可以继续使用Tkinter库&#xff0c;这是一个非常流行且易于使用的GUI库。下面是一个简单的计算器实现&#xff0c;它支持加、减、乘、除四种基本运算。 首先&#xff0c;确保你的Python环境中已经安装了Tkinter。Tkinter通…...

【四范式】浅谈NLP发展的四个范式

自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是计算机科学&#xff0c;人工智能&#xff0c;语言学关于计算机和人类自然语言之间的相互作用的领域&#xff0c;是计算机科学领域与人工智能领域中的一个重要方向。NLP发展到今天已经进入到了…...

--- 数据结构 优先级队列 --- java

之前提高到队列是一种先进先出的结构&#xff0c;但是在某些情况下操作的数据具有优先级&#xff0c;那么对他先进行操作&#xff0c;这时队列就不能满足需求了&#xff0c;因为队列只能操作对头的元素&#xff0c;而具有优先级的数据不一定是在对头&#xff0c;这样就需要优先…...

鸿萌数据恢复服务:如何恢复 Mac 系统中被擦除的文件?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据备份、数据恢复解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 公司是多款国际主流数据恢复软件的授权代理商&#xff0c;为…...

片段阅读2_中心理解以外题型

目录 一、标题拟定二、下文推断1.三种简单结构:2.三种不易识别结构:三、语句填入1.在开头2.在中间3.在尾句4.盯细节四、语句排序1.宏观把握2.盯住细节五、细节判断一、标题拟定 题型说明:主旨意图题的变型,就是把主旨意图进行“标题化”的改造;正确选项要求:标题中需包含…...

【网络安全 | 渗透工具】IIS 短文件名枚举工具—shortscan安装使用教程

未经许可,不得转载。 文章目录 shortscan安装使用Shortutil 工具shortscan ShortScan 是一种用于在 Microsoft IIS (Internet Information Services) Web 服务器上进行短文件名枚举的工具。该工具可以帮助攻击者利用 IIS 的文件名处理特性,通过预测性扫描枚举服务器上的文件…...

数据结构——栈和队列(队列的定义、顺序队列以及链式队列的基本操作)

目录 队列&#xff08;queue&#xff09;的定义 顺序队——队列的顺序表示和实现 顺序队列&#xff08;循环队列&#xff09;的类型定义 顺序队列上溢问题的解决方法 ​编辑 循环队列的基本操作 队列的基本操作——队列的初始化 队列的基本操作——求队列的长度 队列的…...

el-table 的单元格 + 图表 + 排序

<el-table border :data"tableDataThree" height"370px" style"width: 100%"><el-table-column :key"activeName 8" width"50" type"index" label"序号" align"center"></el…...

FPGA第 9 篇,Verilog 中的关键字和基数

前言 在 Verilog 中&#xff0c;关键字&#xff08;Keywords&#xff09;和基数&#xff08;Radix&#xff09;是语言的重要组成部分&#xff0c;它们有助于描述和定义硬件设计。上期分享了 Verilog 的基本使用&#xff0c;以及数据类型、逻辑值和算数运算符的简单应用&#x…...

什么是单元测试?怎么做?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小…...

论文复现--基于LeNet网络结构的数字识别

前言 一直就听说学习深度学习无非就是看论文&#xff0c;然后复现&#xff0c;不断循环&#xff0c;这段时间也看了好几篇论文(虽然都是简单的)&#xff0c;但是对于我一个人自学&#xff0c;复现成功&#xff0c;我感觉还是挺开心的 本人初学看论文的思路&#xff1a;聚焦网络…...

Vue3 响应式工具函数isRef()、unref()、isReactive()、isReadonly()、isProxy()

isRef() isRef()&#xff1a;检查某个值是否为 ref。 isRef函数接收一个参数&#xff0c;即要判断的值。如果该参数是由ref创建的响应式对象&#xff0c;则返回true&#xff1b;否则&#xff0c;返回false。 import { ref, isRef } from vue const normalValue 这是一个普通…...

数据结构之简单选择排序介绍与举例

简单选择排序 简单选择排序是一种排序算法&#xff0c;其基本思想是&#xff1a;通过n-i次关键字间的比较&#xff0c;从n-i1个记录中选出关键字最小的记录&#xff0c;并和第i个记录交换之。 举例&#xff1a; 给定数组 [64, 25, 12, 22, 11]&#xff0c;进行简单选择排序。…...

九、Redis 的实际使用与Redis的设计

一、多级缓存架构 在线上系统中&#xff0c;一定不会单纯的只部署一个Redis集群&#xff0c;而是使用Redis结合其他的多级缓存应用进行架构。 使用多级缓存架构的优点就是可以使不同类型的数据分布在不同的应用中&#xff0c;比如redis的热点key可以存储到nginx本地缓存、服务…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...