如何让析构函数等待其他线程的任务完成?
线程异步任务与对象销毁的线程安全解决方案
你的核心问题是对象生命周期与异步任务执行周期不一致:主线程销毁对象时,子线程可能还在尝试访问该对象的成员(包括mutex),此时对象内存已被释放,任何访问都会触发未定义行为(比如崩溃)。不需要将mutex放在全局作用域,以下是几种更合理的解决方案:
方案1:用智能指针管理对象生命周期(推荐)
通过std::shared_ptr和std::weak_ptr的组合,让子线程在操作对象前先确认对象是否还存活,从根源上避免访问已销毁的内存。
修改后的代码示例:
#include <iostream> #include <thread> #include <mutex> #include <memory> #include <chrono> using namespace std; class Bomb { public: int trigger; mutex my_mutex; }; void f1(weak_ptr<Bomb> wp) { this_thread::sleep_for(chrono::seconds(1)); // 尝试获取对象的强引用,判断对象是否存活 auto sp = wp.lock(); if (!sp) { cout << "对象已销毁,任务终止" << endl; return; } // 对象存活,安全加锁并访问成员 lock_guard<mutex> lock(sp->my_mutex); cout << "wake up.." << sp->trigger << "..." << endl; } int main() { auto b = make_shared<Bomb>(); b->trigger = 1; // 传递weak_ptr给子线程,不延长对象生命周期 thread t1(f1, weak_ptr<Bomb>(b)); this_thread::sleep_for(chrono::seconds(1)); // 释放对象所有权,此时若子线程未持有强引用,对象会被销毁 b.reset(); t1.join(); return 0; }
原理:shared_ptr负责对象的生命周期管理,主线程调用reset()后,若子线程未通过weak_ptr.lock()获取到强引用,对象会被自动销毁;子线程只有在成功获取强引用后,才会执行后续操作,确保对象内存有效。
方案2:外部生命周期控制锁+原子终止标志
如果不想用智能指针,可以用一个生命周期长于Bomb对象的mutex(无需全局,可封装到管理类),配合原子布尔变量标记对象状态,让子线程在访问对象前先确认状态。
示例代码:
#include <iostream> #include <thread> #include <mutex> #include <atomic> #include <chrono> using namespace std; // 可以封装到一个单独的生命周期管理类中,无需全局 mutex lifecycle_mutex; atomic<bool> is_object_alive{true}; class Bomb { public: int trigger; mutex my_mutex; }; void f1(Bomb* b) { this_thread::sleep_for(chrono::seconds(1)); // 先检查对象是否存活 lock_guard<mutex> lifecycle_lock(lifecycle_mutex); if (!is_object_alive) { cout << "对象已销毁,任务终止" << endl; return; } // 对象存活,安全访问 lock_guard<mutex> lock(b->my_mutex); cout << "wake up.." << b->trigger << "..." << endl; } int main() { Bomb* b = new Bomb(); b->trigger = 1; thread t1(f1, b); this_thread::sleep_for(chrono::seconds(1)); // 销毁前标记对象为已死亡 { lock_guard<mutex> lifecycle_lock(lifecycle_mutex); is_object_alive = false; } delete b; t1.join(); return 0; }
原理:主线程在销毁对象前,先通过锁同步标记对象状态,子线程必须先获取该锁并检查状态,确保只有在对象存活时才会访问其成员。
方案3:针对任务队列的取消机制(适配实际场景)
如果你的实际场景是主线程向任务队列发送异步任务,可以在触发销毁时:
- 先清空任务队列中未执行的任务,避免子线程拿到已失效的对象指针;
- 给正在执行的任务添加取消标记,子线程在处理任务前先检查标记,若已触发销毁则直接退出。
这种方案更贴合异步任务队列的场景,从任务调度层面避免无效的对象访问。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

