一种用于多线程中间状态同步的屏障机制

一种用于多线程中间状态同步的屏障机制

为了解决在多线程环境中,需要一个内置的计数屏障对于多个线程中的某一个部分进行检查,确保所有线程均到达该点后才能继续执行。

该屏障常被用于多线程流水线中的中间检查,适用于阶段分割,是一种有效的同步机制。

此处构建了一个barrier类,其中arrive_and_wait()函数是对应的屏障方法,work是测试线程。

此处代码注释掉的是使用busy-wait进行循环的忙等版本,保留了使用条件变量和unique_lock的阻塞wait_for版本,可以对比两者之间的性能差距。

一般来说,线程规模较小,任务量较少时busy-wait效率较高,是由于sleep-awake过程中有系统调用。当任务规模达到一定程度时,wait_for通常性能较好。

代码如下:

复制代码
#include <thread>
#include <unistd.h>
#include <vector>
#include <cstddef>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "barrier.hpp"

class barrier {
public:

    barrier(size_t expected)
    :_expected(expected)
    ,_arrived(0)
    ,_passed(0)
    {}

    void arrive_and_wait();

private:
    const size_t _expected;
    std::atomic<size_t> _arrived;
    std::atomic<size_t> _passed;
    std::mutex mtx;
    std::condition_variable cv;
};

/*void barrier::arrive_and_wait()
{
    auto passed = _passed.load();
    if (_arrived.fetch_add(1) == _expected - 1) {
        // NOTE: reset *before* incrementing, otherwise we might reset to zero a
        // thread already waiting on the next wave
        _arrived = 0;
        _passed++;
    } else {
        while (_passed.load() == passed) {
        // busy-wait
        }
    }
}*/

void barrier::arrive_and_wait()
{
    auto passed = _passed.load();
    if (_arrived.fetch_add(1) == _expected - 1) {
        // NOTE: reset *before* incrementing, otherwise we might reset to zero a
        // thread already waiting on the next wave
        _arrived = 0;
        _passed++;
        cv.notify_all();
    } else { //block
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, [&] {
            return _passed.load() != passed;
        });
        lck.unlock();
        cv.notify_all();
    }
}

void work(size_t id, barrier& b)
{
    printf("+%ld\n", id); fflush(stdout);
    b.arrive_and_wait();
    printf(".%ld\n", id); fflush(stdout);
    b.arrive_and_wait();
    printf("-%ld\n", id); fflush(stdout);
}

int main(int argc, char** argv)
{
    auto nthreads = atoi(argv[1]);
    barrier b(nthreads);
    std::vector<std::thread> threads;

    for (auto i = nthreads; i; i--) {
        threads.emplace_back(work, i, std::ref(b));
    }

    for (auto& thread : threads) {
        thread.join();
    }
}
相关推荐
zh_xuan4 分钟前
c++ 单例模式
开发语言·c++·单例模式
利刃大大2 小时前
【在线五子棋对战】二、websocket && 服务器搭建
服务器·c++·websocket·网络协议·项目
喜欢吃燃面2 小时前
C++刷题:日期模拟(1)
c++·学习·算法
SHERlocked932 小时前
CPP 从 0 到 1 完成一个支持 future/promise 的 Windows 异步串口通信库
c++·算法·promise
虚拟之4 小时前
36、stringstream
c++
我很好我还能学4 小时前
【面试篇 9】c++生成可执行文件的四个步骤、悬挂指针、define和const区别、c++定义和声明、将引用作为返回值的好处、类的四个缺省函数
开发语言·c++
南岩亦凛汀5 小时前
在Linux下使用wxWidgets进行跨平台GUI开发
c++·跨平台·gui·开源框架·工程实战教程
曦月逸霜5 小时前
第34次CCF-CSP认证真题解析(目标300分做法)
数据结构·c++·算法
galaxy_strive5 小时前
绘制饼图详细过程
开发语言·c++·qt
Unpredictable2227 小时前
【VINS-Mono算法深度解析:边缘化策略、初始化与关键技术】
c++·笔记·算法·ubuntu·计算机视觉