You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让析构函数等待其他线程的任务完成?

线程异步任务与对象销毁的线程安全解决方案

你的核心问题是对象生命周期与异步任务执行周期不一致:主线程销毁对象时,子线程可能还在尝试访问该对象的成员(包括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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 11:45:34