为何此C++ Barrier实现无法正常工作?求解析问题原因
这份C++ Barrier实现失效的原因分析
先看有问题的实现代码:
void JoinQuery::barrier() { std::unique_lock<std::mutex> lk(barrier_mutex); barrier_count++; if (barrier_count == NUM_THREADS) { barrier_count = 0; lk.unlock(); barrier_cv.notify_all(); } else { barrier_cv.wait(lk, [this] { return barrier_count == 0; }); } }
核心问题:跨轮次的条件判断冲突
这个实现的致命问题出在线程完成一轮barrier后,可能立刻进入下一轮,干扰还在处理上一轮唤醒的线程。
举个具体例子(假设NUM_THREADS=3):
- 第一轮:线程1、2先后进入,
barrier_count变成2,都进入wait等待。 - 线程3进入,
barrier_count变为3,触发重置为0,解锁后notify_all。 - 线程1被唤醒,拿到锁检查
barrier_count==0,条件满足,退出wait,释放锁后立刻再次调用barrier()——此时barrier_count被改成1,线程1又进入wait。 - 线程2这时候被唤醒,拿到锁检查条件,发现
barrier_count已经是1(被线程1的新一轮操作修改),不满足==0,于是继续等待。 - 此时线程2被卡在wait中,而新一轮的线程3还没进来,永远不会触发notify_all,导致死锁。
为什么需要第二个变量?
第二个变量(通常叫generation或round)的作用是标记barrier的轮次,让线程只等待当前轮次的结束,不会被下一轮的操作干扰。比如修改后的逻辑:
- 每个线程进入barrier时,记录当前的
generation值。 - wait的条件改成「当前
generation不等于自己记录的值」,而不是依赖barrier_count。 - 最后一个线程到达时,不仅重置
barrier_count,还递增generation,再notify_all。
这样,上一轮的线程被唤醒后,只要generation变了,就知道自己已经完成当前轮次,不会被下一轮的barrier_count变化影响,能正常退出。
内容的提问来源于stack exchange,提问作者Sea Erchin
相关产品推荐
相关产品推荐

