Boost Thread构造后启动线程,线程校验断言失败求优化方案
解决Debug构建中线程执行断言失败的问题
这个问题我之前也碰到过,核心原因确实是你分析的第2点:主线程给m_thread赋值的操作,和子线程执行doStart的代码存在竞态——子线程可能在m_thread还没完成赋值(此时还是默认构造的空线程对象)时就执行了断言,自然会和当前线程ID不匹配导致失败。另外补充下:Boost线程的拷贝构造函数是被禁用的,所以你担心的第3种原因(拷贝构造改变ID)可以直接排除;而get_id()本身是线程安全的,第1种内部竞态的可能性也极低,所以重点就是解决赋值和子线程执行的同步问题。
下面给你几个比忙等更优雅的解决方案:
方案1:用std::promise/std::future实现同步
这种方法通过同步信号确保子线程在主线程完成m_thread赋值后,再执行断言逻辑,完全消除竞态:
#include <boost/thread.hpp> #include <future> class MyClass { private: boost::thread m_thread; std::promise<void> m_startup_sync; void doStart(std::future<void> sync_future) { // 阻塞等待主线程完成m_thread赋值 sync_future.get(); // 此时m_thread已完全初始化,断言安全 assert(m_thread.get_id() == boost::this_thread::get_id()); // ... doStart的其他业务逻辑 } public: void start() { // 创建同步用的future并传递给子线程 std::future<void> sync_future = m_startup_sync.get_future(); // 创建子线程(此时子线程会启动但立即阻塞在get()) m_thread = boost::thread(&MyClass::doStart, this, std::move(sync_future)); // 主线程完成m_thread赋值后,通知子线程继续执行 m_startup_sync.set_value(); } };
这个方案的优点是同步逻辑清晰,没有忙等带来的CPU浪费,而且是标准C++组件(除了Boost线程),兼容性好。
方案2:用boost::barrier实现屏障同步
如果你的项目已经依赖Boost,可以用boost::barrier来实现主线程和子线程的同步:
#include <boost/thread.hpp> #include <boost/thread/barrier.hpp> class MyClass { private: boost::thread m_thread; boost::barrier m_startup_barrier{2}; // 初始化等待2个线程 void doStart() { // 等待主线程到达屏障 m_startup_barrier.wait(); // 此时m_thread已完成赋值,断言安全 assert(m_thread.get_id() == boost::this_thread::get_id()); // ... doStart的其他业务逻辑 } public: void start() { m_thread = boost::thread(&MyClass::doStart, this); // 主线程完成m_thread赋值后,到达屏障,子线程即可继续 m_startup_barrier.wait(); } };
屏障的思路很直观:让两个线程在各自完成准备工作后再继续执行,适合这种简单的双向同步场景。
额外说明
- 关于你提到的第3种原因:Boost线程从1.50版本开始就支持移动语义了,
m_thread = boost::thread(...)这种赋值会调用移动赋值运算符,不会触发拷贝构造(拷贝构造是被删除的),所以完全不用担心线程ID被意外改变。 - 如果你的项目已经升级到C++11及以上,也可以直接用
std::thread替代boost::thread,上述方案的同步逻辑完全通用。
内容的提问来源于stack exchange,提问作者Raven
相关产品推荐
相关产品推荐

