标准库标头 <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_type
        is_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_release
        ptrdiff_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_acquire
            const 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_rel
        ptrdiff_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.mfbz.cn/a/879688.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot项目实现Docker容器化部署

将Spring Boot项目部署到Docker容器中的涉及几个主要步骤&#xff1a; 准备Docker镜像 首先&#xff0c;需要选择一个基础镜像&#xff0c;通常是包含Java运行时环境的镜像&#xff0c;例如OpenJDK。可以从Docker Hub或其他镜像仓库中获取这些镜像。接下来&#xff0c;需要在…

C++库文件移植到QT中一直出错

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【软考】数据字典(DD)

目录 1. 说明2. 数据字典的内容2.1 说明2.2 数据流条目2.3 数据存储条目2.4 数据项条目2.5 基本加工条目 3. 数据词典管理4. 加工逻辑的描述4.1 说明4.2 结构化语言4.3 判定表4.3 判定树 5. 例题5.1 例题1 1. 说明 1.数据流图描述了系统的分解&#xff0c;但没有对图中各成分进…

一个基于 laravel 和 amis 开发的后台框架, 友好的组件使用体验,可轻松实现复杂页面(附源码)

前言 随着互联网应用的发展&#xff0c;后台管理系统的复杂度不断增加&#xff0c;对于开发者而言&#xff0c;既要系统的功能完备&#xff0c;又要追求开发效率的提升。然而&#xff0c;传统的开发方式往往会导致大量的重复劳动&#xff0c;尤其是在构建复杂的管理页面时。有…

【移动端开发】“明日头条APP”

文章目录 1 系统概述1.1研究背景1.2研究意义 2 系统设计2.1 关键技术2.2 系统设计2.2.1 系统功能模块2.2.2 数据库设计 3 系统实现3.1 数据模型3.1.1 NewsURL3.1.2 NewsType3.1.3 NewsInfo 3.2 数据库操作3.2.1 DBOpenHelper3.2.2 DBManager 3.3 适配器类3.3.1 AddItem3.3.2 In…

Redhat 7,8,9系(复刻系列) 一键部署Oracle19c rpm

Oracle19c前言 Oracle 19c 是甲骨文公司推出的一款企业级关系数据库管理系统,它带来了许多新的功能和改进,使得数据库管理更加高效、安全和可靠。以下是关于 Oracle 19c 的详细介绍: 主要新特性 多租户架构:支持多租户架构,允许多个独立的数据库实例在同一个物理服务器上…

【机器学习】9 ——最大熵模型的直观理解

机器学习9 ——最大熵模型的直观理解 文章目录 机器学习9 ——最大熵模型的直观理解前奏例子硬币垃圾邮件代码 前奏 【机器学习】6 ——最大熵模型 例子 硬币 假设我们有一枚硬币&#xff0c;可能是公平的&#xff0c;也可能是不公平的。我们的任务是估计硬币的正反面出现的…

通过Python代码发送量化交易信号邮件通知

量化交易利用数学模型和计算机算法来分析市场数据,并生成交易信号,本文将介绍如何使用Python编写一个简单的脚本,通过发送邮件通知量化交易信号。 开启SMTP服务 首先要在发件箱的邮件设置中,将POP3/SMPT服务开启,记录下授权密码,在本地可通过此密码登录,注意有效期和保…

微信小程序页面制作——婚礼邀请函(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【网络】TCP/IP 五层网络模型:网络层

最核心的就是 IP 协议&#xff0c;是一个相当复杂的协议 TCP 详细展开讲解&#xff0c;是因为 TCP 确实在开发中非常关键&#xff0c;经常用到&#xff0c;IP 则不同&#xff0c;和普通程序猿联系比较浅。和专门开发网络的程序猿联系比较紧密&#xff08;开发路由器&#xff0…

3款免费的GPT类工具

前言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;的崛起与发展已经成为我们生活中不可或缺的一部分。它的出现彻底改变了我们与世界互动的方式&#xff0c;并为各行各业带来了前所未有的便利。 一、Kimi 网址&#xff1a;点我前往 国产AI模型Kimi是一…

neo4j安装启动教程+对应的jdk配置

参考这位博主的视频教程&#xff1a;neo4j社区windows版下载 一、官网下载neo4j的安装包 &#xff08;1&#xff09;官网下载页面 &#xff08;2&#xff09;上一步 【download】之后&#xff0c;会自动下载&#xff0c;如果没有&#xff0c;点击【here】 这里可以看到一行字…

Qwen 2.5:阿里巴巴集团的新一代大型语言模型

Qwen 2.5&#xff1a;阿里巴巴集团的新一代大型语言模型 摘要&#xff1a; 在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;的发展日新月异&#xff0c;它们在自然语言处理&#xff08;NLP&#xff09;和多模态任务中扮演着越来越重要的角色。阿里巴巴集…

获取参数

获取querystring参数 querystring 指的是URL中 ? 后面携带的参数&#xff0c;例如&#xff1a;http://127.0.0.1:9090/web?query杨超越。 获取请求的querystring参数的方法如下&#xff1a; 方法1&#xff1a; Query package main// querystringimport ("github.com/…

有毒有害气体检测仪的应用和性能_鼎跃安全

随着现代工业的不断发展和扩张&#xff0c;越来越多的企业涉及到有毒有害气体的生产、使用和处理。工业规模的扩大导致有毒有害气体的排放量增加&#xff0c;同时也增加了气体泄漏的风险。在发生火灾、爆炸或危险化学品泄漏等紧急事件时&#xff0c;救援人员需要迅速了解现场的…

python+flask+mongodb+vue撸一个实时监控linux服务资源的网站

用pythonflaskmongodbvue写一个监控linux服务资源实时使用率的页面网站&#xff0c;并每30秒定时请求&#xff0c;把Linux数据保存数据到mongodb数据库中&#xff0c;监控的linux的资源有&#xff1a;cup、内存、网络带宽、mysql慢查询、redis、系统平均负载、磁盘使用率等&…

百度Android IM SDK组件能力建设及应用

作者 | 星途 导读 移动互联网时代&#xff0c;随着社交媒体、移动支付、线上购物等行业的快速发展&#xff0c;对即时通讯功能的需求不断增加。对于各APP而言&#xff0c;接入IM SDK&#xff08;即时通讯软件开发工具包&#xff09;能够大大降低开发成本、提高开发效率&#…

动手学习RAG:大模型重排模型 bge-reranker-v2-gemma微调

动手学习RAG: 向量模型动手学习RAG: moka-ai/m3e 模型微调deepspeed与对比学习动手学习RAG&#xff1a;rerank模型微调实践 bge-reranker-v2-m3动手学习RAG&#xff1a;迟交互模型colbert微调实践 bge-m3动手学习RAG: 大模型向量模型微调 intfloat/e5-mistral-7b-instruct动手学…

Leetcode Hot 100刷题记录 -Day14(矩阵置0)

矩阵置0 问题描述&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&#xff1a;…

模版进阶(template)

1.非类型模版参数 模版参数分类类型形参与非类型形参。 ① 类型形参&#xff1a;出现在在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 ② 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当…