C++中std::jthread作为类成员的销毁顺序是否存在问题?
问题解答:线程同步类的析构顺序风险
这种情况必然会引发严重问题,属于C++标准定义的未定义行为,具体原因和解决方法如下:
析构顺序与风险根源
类成员的析构顺序是声明顺序的逆序:你的代码中threads是第一个声明的成员,因此会最后被析构;而cv、mut在threads之后声明,会先于threads完成析构。std::jthread的析构函数默认会自动join所有线程(即等待线程执行完毕),这意味着在cv和mut已经被销毁的时间段内,线程可能还在运行。如果线程中存在任何访问mut或cv的操作(比如加锁、解锁、调用wait()/notify_one()等),本质就是访问已经被销毁的对象,这会直接触发未定义行为——程序可能崩溃、出现内存损坏、行为异常,甚至在不同环境下表现出完全不同的结果。正确的解决方案
调整成员变量的声明顺序,把std::vector<std::jthread> threads放在最后一位。这样析构时会先执行threads的析构函数,等待所有线程完全结束后,再销毁cv、mut等同步变量,从根本上避免线程访问已销毁对象的风险。
修正后的代码示例:
class ThreadHandler { std::mutex mut; std::condition_variable cv; int some_int; // some other synchronization related variables std::vector<std::jthread> threads; // 放在最后,确保线程先结束,再销毁同步资源 };
- 额外注意事项
即便你认为线程逻辑上已经不再使用同步变量,也绝对不能提前销毁mut和cv——线程的执行状态无法被完全预判,哪怕是最后一次解锁mutex的操作,若此时mutex已销毁,依然会导致未定义行为。必须保证所有依赖的同步资源生命周期覆盖线程的整个运行周期。
内容的提问来源于stack exchange,提问作者Bobby
相关产品推荐
相关产品推荐

