Go Barrier栅栏

1. 简介

实现与pythonthreading.Barrier库类似的功能,多线程同时等待达到指定数量一起放行。

有待改进地方

  1. wait方法没有支持context控制。

2. 代码

import (
	"context"
	"golang.org/x/sync/semaphore"
	"sync/atomic"
)

type Barrier struct {
	count  int64 // 记录当前多少
	amount int64 // 记录多少放行
	entry  *semaphore.Weighted
	exit   *semaphore.Weighted
}

func NewBarrier(n int64) *Barrier {
	b := &Barrier{
		count:  0,
		amount: n,
		entry:  semaphore.NewWeighted(n),
		exit:   semaphore.NewWeighted(n),
	}
	_ = b.exit.Acquire(context.Background(), n)
	return b
}

func (b *Barrier) Wait() {
	ctx := context.Background()

	// 限制进入数量
	_ = b.entry.Acquire(context.Background(), 1)

	// 如果是最后一个人,放行前面所有包括自己。
	if atomic.AddInt64(&b.count, 1) == b.amount {
		defer func() {
			b.count = 0
			b.entry.Release(b.amount)
		}()

		// 放行所有
		b.exit.Release(b.amount)
	}

	// 等待放行
	_ = b.exit.Acquire(ctx, 1)
}

测试

func TestBarrier(t *testing.T) {
	b := NewBarrier(2)
	for i := 1; i <= 10; i++ {
		go func(id int) {
			b.Wait()
			t.Log("waited", id)
		}(i)
		time.Sleep(time.Second)
	}
}
<think>我们正在讨论C++中的栅栏barrier)用法和实现。C++20在<barrier>头文件中引入了std::barrier类,用于同步多个线程,使它们在某一点上等待,直到所有参与的线程都到达该点,然后一起继续执行。引用中并没有直接提到barrier,但我们可以根据已有的并发知识来回答。栅栏barrier)是一种同步机制,它要求多个线程在某个点等待,直到所有线程都到达这个点,然后它们才能一起继续执行。这在并行计算中非常有用,比如在分阶段处理的算法中,需要确保所有线程完成当前阶段才能进入下一阶段。C++20中引入了std::barrier,其基本用法如下:1.创建一个std::barrier对象,指定需要等待的线程数量(计数)。2.每个线程在需要同步的地方调用arrive_and_wait()方法,该方法会阻塞直到所有线程都到达。3.也可以使用arrive()和wait()分开操作,但通常arrive_and_wait()更方便。此外,std::barrier还允许在每次所有线程到达屏障点时执行一个完成函数(completionfunction)。这个函数会在所有线程被释放之前执行,且由其中一个线程执行。下面是一个简单的示例,展示如何使用std::barrier:```cpp#include<barrier>#include<iostream>#include<thread>#include<vector>intmain(){constintnum_threads=3;std::barriersync_point(num_threads);//创建栅栏,计数为线程数autoworker=[&](intid){std::cout<<"Thread"<<id<<"startingphase1\n";//模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(100));//到达栅栏并等待sync_point.arrive_and_wait();std::cout<<"Thread"<<id<<"startingphase2\n";//模拟第二阶段工作std::this_thread::sleep_for(std::chrono::milliseconds(100));//再次等待sync_point.arrive_and_wait();std::cout<<"Thread"<<id<<"finished\n";};std::vector<std::thread>threads;for(inti=0;i<num_threads;++i){threads.emplace_back(worker,i);}for(auto&t:threads){t.join();}}```在这个例子中,我们创建了3个线程,每个线程执行两个阶段的工作。在每个阶段结束后,线程都会调用arrive_and_wait()等待其他线程。当所有线程都到达栅栏后,它们才会一起进入下一个阶段。注意:std::barrier的计数在构造时确定,但也可以使用arrive_and_drop()来减少计数(当某个线程提前退出时)。如果没有C++20的支持,我们可以使用其他同步原语来实现栅栏,比如条件变量和互斥锁。下面是一个简单的实现:```cppclassBarrier{public:explicitBarrier(intcount):count_(count),spaces_(count),generation_(0){}voidwait(){std::unique_lock<std::mutex>lock(mutex_);intgen=generation_;if(--spaces_==0){spaces_=count_;generation_++;cond_.notify_all();}else{cond_.wait(lock,[this,gen]{returngen!=generation_;});}}private:std::mutexmutex_;std::condition_variablecond_;intcount_;intspaces_;intgeneration_;};```这个实现使用了一个互斥锁和一个条件变量。每个线程调用wait()时,将剩余等待数(spaces_)减1。如果减到0,则重置等待数并增加代次(generation_),然后唤醒所有等待的线程。否则,线程等待直到代次改变(表示所有线程都到达了)。使用这个自定义栅栏的示例与上面类似,只需将std::barrier替换为Barrier,并调用wait()方法。栅栏在并行算法中非常有用,例如在并行排序、并行数值计算等需要阶段同步的场景。相关问题:1.C++20中std::barrier和std::latch有什么区别?2.如何使用条件变量和互斥锁实现一个可重用的栅栏?3.在多线程编程中,栅栏和信号量(semaphore)有何不同?4.在并行计算中,栅栏同步可能带来什么性能问题?希望这些信息对你有所帮助。</think>### C++栅栏Barrier)用法与实现 #### 一、栅栏的概念 栅栏Barrier)是一种线程同步机制,要求**指定数量的线程全部到达同步点**后才能继续执行。其核心作用是协调多个线程的执行阶段,常用于**分阶段并行算法**(如并行排序、数值计算等)[^1]。 #### 二、C++20 标准库实现 C++20 在 `<barrier>` 头文件中正式引入 `std::barrier`。基本用法: ```cpp #include <barrier> #include <thread> #include <vector> int main() { const int num_threads = 4; std::barrier sync_point(num_threads); // 初始化栅栏计数 auto worker = [&](int id) { // 第一阶段工作 std::cout << "Thread " << id << " completed phase 1\n"; sync_point.arrive_and_wait(); // 等待所有线程到达 // 第二阶段工作(所有线程同步后执行) std::cout << "Thread " << id << " completed phase 2\n"; }; std::vector<std::thread> threads; for (int i = 0; i < num_threads; ++i) { threads.emplace_back(worker, i); } for (auto& t : threads) t.join(); } ``` **关键方法**: - `arrive_and_wait()`:线程到达栅栏并阻塞等待 - `arrive_and_drop()`:到达后减少后续栅栏计数(线程退出时使用) #### 三、手动实现栅栏(C++20之前) 使用互斥锁(mutex)和条件变量(condition variable)实现: ```cpp #include <mutex> #include <condition_variable> class Barrier { public: explicit Barrier(int count) : threshold(count), count(count), generation(0) {} void wait() { std::unique_lock<std::mutex> lock(mutex); int gen = generation; if (--count == 0) { count = threshold; // 重置计数器 generation++; // 更新代际 cond.notify_all(); // 唤醒所有线程 return; } cond.wait(lock, [this, gen] { return gen != generation; // 防止虚假唤醒 }); } private: std::mutex mutex; std::condition_variable cond; int threshold; // 初始线程数 int count; // 剩余等待数 int generation; // 代际计数器 }; ``` **实现原理**: 1. 线程调用 `wait()` 时减少 `count` 2. 当 `count` 归零时: - 重置计数器 - 递增 `generation`(区分不同同步周期) - 唤醒所有等待线程 3. 使用 `generation` 避免**虚假唤醒** #### 四、典型应用场景 1. **并行算法分阶段同步** ```cpp // 并行矩阵乘法伪代码 void parallel_multiply() { // 阶段1: 局部计算 barrier.arrive_and_wait(); // 阶段2: 交换边界数据 barrier.arrive_and_wait(); // 阶段3: 全局归约 } ``` 2. **多线程资源初始化** ```cpp void init_resource(int id) { load_local_config(id); // 加载局部配置 barrier.arrive_and_wait(); verify_global_state(); // 依赖全局状态 } ``` #### 五、性能注意事项 1. **避免嵌套栅栏**:可能导致死锁 2. **匹配线程数**:栅栏计数需与实际线程数一致 3. **替代方案**:轻量级场景可考虑 `std::latch`(一次性栅栏) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yimtcode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值