为何静态std::barrier可用,动态shared_ptr实现却挂起?附可用代码
问题背景
我编写了两段用std::barrier{2}同步2个线程的C代码:静态创建barrier的版本能正常运行,但最初用std::shared_ptr动态创建的版本会出现挂起问题。由于线程数(即barrier的计数)只能在运行时确定,无法静态声明,因此必须采用动态实现方式。编译运行命令为:clang++-15 -l pthread --std=c++20 demo.cpp && ./a.out(也使用过g-11)。
挂起原因分析
静态版本正常是因为barrier对象的生命周期贯穿整个程序运行,线程调用arrive_and_wait()时对象始终有效。而最初的动态版本挂起,最常见的原因是barrier对象在线程完成等待前被提前销毁:
- 如果用
shared_ptr时,线程捕获的是barrier的引用而非副本,主线程若提前释放shared_ptr(比如调用reset()或让其离开作用域),会导致barrier的引用计数归0,对象被析构。此时线程访问已销毁的barrier,触发未定义行为,表现为挂起或程序崩溃。 - 另一种可能是运行时确定的线程数与barrier初始计数不匹配:比如实际启动的线程数少于barrier计数,会导致已启动的线程永远等待足够的到达信号;若线程数多于计数,多余线程的
arrive_and_wait()也会挂起。
错误示例(导致挂起的代码)
#include <barrier> #include <thread> #include <memory> int main() { auto barrier = std::make_shared<std::barrier<>>(2); // 线程捕获barrier的引用,主线程若提前释放,会访问无效对象 std::thread t1([&]() { barrier->arrive_and_wait(); }); std::thread t2([&]() { barrier->arrive_and_wait(); }); // 错误:主线程提前重置shared_ptr,导致barrier被销毁 barrier.reset(); t1.join(); t2.join(); return 0; }
修复后的正确动态版本
#include <barrier> #include <thread> #include <memory> int main() { auto barrier = std::make_shared<std::barrier<>>(2); // 线程捕获shared_ptr副本,保证持有引用,barrier不会被提前销毁 std::thread t1([barrier]() { barrier->arrive_and_wait(); }); std::thread t2([barrier]() { barrier->arrive_and_wait(); }); t1.join(); t2.join(); return 0; }
额外注意事项
- 确保运行时确定的线程数与barrier的初始计数完全一致,避免因计数不匹配导致的挂起。
- 若使用barrier的完成阶段(completion step),需确保该阶段不会抛出未捕获的异常,否则可能触发未定义行为。
内容的提问来源于stack exchange,提问作者CIsForCookies
相关产品推荐
相关产品推荐

