并行std::for_each数据竞争疑问:cppreference示例代码错误解析
为什么这个并行
for_each示例是错误的? 你忽略了std::execution::par执行策略的一个关键细节:它不保证任务一定会并行执行。标准允许实现选择串行执行这些任务,而这正是导致代码死锁的核心原因。
让我们拆解两种执行场景:
场景1:你假设的并行执行(能正常运行的情况)
如果实现用两个线程分别处理数组的两个元素:
- 线程1执行
x.fetch_add(1),x变为1,接着进入while循环等待x变成2 - 线程2执行
x.fetch_add(1),x变为2,此时两个线程的循环条件都不满足,顺利退出
这种情况下代码确实能正常运行,但这只是其中一种可能的执行路径。
场景2:串行执行(你没考虑到的致命情况)
如果实现选择串行执行两个lambda(比如系统资源不足,或者实现本身倾向于串行处理小任务):
- 第一个lambda执行:
x.fetch_add(1)让x变成1,接着进入while (x.load() == 1)循环 - 此时第二个lambda还没有被执行,x的值会一直是1,这个循环会无限运行下去,直接导致程序卡死
这就是代码被标注为错误的本质:它隐含假设了任务一定会被并行执行,但par策略并没有给出这个保证。
额外补充:即使并行也有潜在问题
退一步说,就算是并行执行,线程调度的随机性也可能导致逻辑不符合预期——比如某个线程的fetch_add完成后,另一个线程还没开始执行,等待的线程会空转很久,但最致命的还是串行执行时的死锁问题。
怎么修复?
不要依赖执行策略的并行性来实现同步逻辑,改用明确的同步原语(比如std::latch或std::barrier)来协调任务:
std::atomic<int> x{0}; int a[] = {1,2}; std::latch latch(2); // 等待2个任务到达同步点 std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int) { x.fetch_add(1, std::memory_order_relaxed); latch.count_down(); latch.wait(); // 等待所有任务完成fetch_add步骤 });
这样不管是串行还是并行执行,每个任务都会先完成fetch_add,再等待所有任务都到达同步点,彻底避免了死锁风险。
内容的提问来源于stack exchange,提问作者bartop
相关产品推荐
相关产品推荐

