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

news/2024/10/4 18:35:46/文章来源:https://blog.csdn.net/chenyijun/article/details/139996938

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

类模板 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博客

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

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

相关文章

vmvare如何给centos7 设置静态IP地址

本章教程,主要介绍如何在vmvare中如何给虚拟机中设置静态IP地址。本章教程中使用的linux发行版是centos7。 目前没有静态IP地址,并且不能联网,此时我们需要给它配置一个静态IP,并且可以实现联网功能。 一、前置步骤 1、网络设置 2、添加网络 添加一个虚拟机网络,选择VMne…

初探全同态加密1 —— FHE的定义与历史回顾

文章目录 一、加密体系1、什么是加密体系2、加密体系的属性 Properties 二、同态加密&#xff1a;偶然的特殊性质三、同态加密体系的分类四、部分同态加密 Partially Homomorphic Encryption1、加法同态加密算法 —— ElGamal 加密算法1.1、ElGamal 的大致步骤1.2、ElGamal 的加…

OpenCore Legacy Patcher 2.0.0 发布,83 款不受支持的 Mac 机型将能运行最新的 macOS Sequoia

在不受支持的 Mac 上安装 macOS Sequoia (OpenCore Legacy Patcher v2.0.0) Install macOS on unsupported Macs 请访问原文链接&#xff1a;https://sysin.org/blog/install-macos-on-unsupported-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主…

推荐5个转换工具,玩转各种音频格式

音频格式多种多样&#xff0c;不同的音频格式在压缩方式、文件大小、音质、兼容性等方面存在差异&#xff0c;如在文件大小方面&#xff0c;WAV格式的音频最大&#xff0c;MP3格式相对较小。 如果你需要压缩音频的话&#xff0c;可以选择FLAC或者WAV的无损压缩方式&#xff0c…

【多模态融合】【NeurIPS 2021】Attention Bottlenecks for Multimodal Fusion

Attention Bottlenecks for Multimodal Fusion 多模态融合的注意力瓶颈 NeurIPS’2021 论文链接 代码链接 摘要 人类通过同时处理和融合来自视觉和音频等多种模态的高维输入来感知世界。与之形成鲜明对比的是&#xff0c;机器感知模型通常是模态特定的&#xff0c;并且针对…

Linux(7)--目录文件的创建、删除、移动、复制、重命名

文章目录 1. 创建目录、文件2. 删除目录、文件3. 移动目录、文件4. 复制目录、文件5. 重命名目录、文件 1. 创建目录、文件 使用mkdir创建目录&#xff1a; 使用touch创建文件&#xff1a; 2. 删除目录、文件 使用rm可以删除文件: 使用rm -f可以强制删除文件&#xff0c;…

基于代理的分布式身份管理方案

目的是使用分布式的联合计算分发去替换掉区块链中原有的类第三方可信中心的证书机制&#xff0c;更加去中心化。 GS-TBK Group Signatures with Time-bound Keys. CS-TBK 算法 Complete subtree With Time-bound Keys&#xff0c;该算法是用来辅助检测用户的签名是否有效&…

Android插件化(三)基础之Android应用程序资源的编译和打包过程分析

Android插件化(三)基础之Android应用程序资源的编译和打包过程分析 Android资源加载常规思路 getResourcesForApplication //首先&#xff0c;通过包名获取该包名的Resources对象 Resources res pm.getResourcesForApplication(packageName); //根据约定好的名字&#xff0c…

少儿编程小游戏 | Scratch《物理投篮》

在线玩&#xff1a;Scratch少儿编程游戏 : 《物理投篮》免费下载-小虎鲸Scratch资源站 在Scratch编程的世界里&#xff0c;孩子们不仅能学习编程知识&#xff0c;还能通过动手实践开发有趣的小游戏。而今天要介绍的这款少儿编程小游戏——《物理投篮》&#xff0c;正是一个集趣…

2024重生之回溯数据结构与算法系列学习【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】

目录 数据结构王道第2章之顺序表 顺序表的定义和基本操作 定义&#xff1a; 基本操作&#xff1a; 基本操作&#xff1a; ​编辑 顺序表的实现-静态分配​编辑 顺序表的静态分配初始化 如果“数组”存满了怎么办&#xff1a; 顺序表的实现-动态分配&#xff1a; ​编辑 顺序表…

开发小程序

由于之前购入的阿里云ECS放着落灰&#xff0c;碰巧又看到个有趣的项目&#xff0c;于是就做了个生成头像的小程序…由于第一次完整发布小程序&#xff0c;记录一下遇到的问题 小程序名称&#xff1a;靓仔创意头像 &#x1f602; 关于小程序 接口请求&#xff0c;在开发过程中…

mysql使用sql函数对json数组的处理

MySQL从5.7版本开始增加了对JSON数据类型的支持。你可以使用->>操作符和JSON_EXTRACT函数来访问JSON数据中的值。 但是&#xff0c;对于JSON数组&#xff0c;如果你想要获取数组中的所有元素&#xff0c;MySQL并没有直接的函数来返回数组中的所有元素作为单独的行。不过…

【网络原理】Tcp 常用提升效率机制——滑动窗口,快速重传,流量控制, 拥塞控制, 建议收藏 !!!

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

[网络层]-IP协议相关特性

IP协议 基本概念 主机 : 配有IP地址,但是不进行路由控制的设备路由器 : 既配有IP地址,又能进行路由控制节点: 主机和路由器的统称 协议头格式 4位版本(version):占四位,用于指定IP协议的版本,例如,使用IPv4,该字段就为44位首部长度: 表示IP协议首部的长度,以32位bit (4字节)…

Redis(redis基础,SpringCache,SpringDataRedis)

文章目录 前言一、Redis基础1. Redis简介2. Redis下载与安装3. Redis服务启动与停止3 Redis数据类型4. Redis常用命令5. 扩展数据类型 二、在Java中操作Redis1. Spring Data Redis的使用1.1. 介绍1.2. 环境搭建1.3. 编写配置类&#xff0c;创建RedisTemplate对象1.4. 通过Redis…

CICD 持续集成与持续交付

目录 一 CICD是什么 1.1 持续集成&#xff08;Continuous Integration&#xff09; 1.2 持续部署&#xff08;Continuous Deployment&#xff09; 1.3 持续交付&#xff08;Continuous Delivery&#xff09; 二 git工具使用 2.1 git简介 2.2 git 工作流程 三 部署git …

10 - UDP实验

在本章节中&#xff0c;我们将采用 network 与 socket 这两个第三方库来构建UDP网络连接的功能。具体而言&#xff0c;network 库将被应用于WiFi连接的建立&#xff0c;而 socket 库则基于 lwIP 协议栈来实现网络协议的连接。在实验环节&#xff0c;我们将利用 ESP32 开发板与远…

【吊打面试官系列-MySQL面试题】CHAR 和 VARCHAR 的区别?

大家好&#xff0c;我是锋哥。今天分享关于【CHAR 和 VARCHAR 的区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; CHAR 和 VARCHAR 的区别&#xff1f; 1、CHAR 和 VARCHAR 类型在存储和检索方面有所不同 2、CHAR 列长度固定为创建表时声明的长度&#xf…

Java项目实战II基于Java+Spring Boot+MySQL的校园社团信息管理系统(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在当今高校…

【Tomcat源码分析】启动过程深度解析 (二)

前言 前文已述&#xff0c;Tomcat 的初始化由 Bootstrap 反射调用 Catalina 的 load 方法完成&#xff0c;包括解析 server.xml、实例化各组件、初始化组件等步骤。此番&#xff0c;我们将深入探究 Tomcat 如何启动 Web 应用&#xff0c;并解析其加载 ServletContextListener …