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

C++线程管理问题:移除vector中Wait对象致主线程提前退出

C++线程管理:移除vector中Wait对象导致主线程提前退出的问题

问题描述

学习规范的C++线程管理时编写示例代码:Holder类创建Wait实例作为任务,每个Wait会启动线程等待指定时长后调用Holder的回调函数进行清理。将Wait实例存入std::vector,回调时取消注释waitlist.erase(it)移除对象,会导致主线程提前退出。

问题原因

核心问题出在Wait类的std::thread成员生命周期管理:

  • Wait对象被erase销毁时,其内部的std::thread成员若处于joinable状态(即线程已启动但未调用join()或detach()),std::thread的析构函数会直接调用std::terminate()终止整个程序。
  • 示例中,线程执行完回调后并未被join或detach,仍处于joinable状态,因此销毁Wait对象时触发程序终止。

正确的移除方式

1. 确保Wait对象销毁前线程已被正确回收

在Wait类中添加析构函数,主动join线程,确保线程完全结束后再销毁对象:

~Wait() {
    if (thread.joinable()) {
        thread.join();
    }
}

这样即使在回调中erase对象,析构时会等待线程执行完毕,避免触发std::terminate()。

2. 多任务场景补充:避免索引失效

如果后续扩展多任务,用id作为vector索引的方式会在erase后失效(后续元素索引会前移),建议改用以下方案:

  • 使用std::list存储Wait对象,erase操作不会导致其他迭代器失效
  • 用std::vector<std::unique_ptr<Wait>>存储智能指针,移除时仅销毁指针,不会影响其他元素索引

修改后的完整代码

#include <chrono>
#include <ctime>
#include <functional>
#include <iostream>
#include <thread>
#include <vector>

using Time = std::chrono::system_clock;
using Seconds = std::chrono::seconds;
using Timepoint = Time::time_point;

class Wait {
private:
    Timepoint target;
    std::thread thread;

public:
    Wait(unsigned int waitFor, unsigned short id, std::function<void(unsigned int)> callback_)
    {
        std::cout << "new Wait objet needs to wait for: " << waitFor << " seconds" << std::endl;
        target = Time::now() + Seconds(waitFor);

        thread = std::thread([callback_, waitFor, id]() {
            std::this_thread::sleep_for(Seconds(waitFor));
            std::cout << "from thread: " << std::this_thread::get_id() << std::endl;
            callback_(id);
        });
    }

    // 添加析构函数,确保线程被正确回收
    ~Wait() {
        if (thread.joinable()) {
            thread.join();
        }
    }

    bool isDone() 
    {
        Timepoint now = Time::now();
        std::chrono::duration<float> difference = now - target;
        return (difference.count() > 0);
    }

    // 禁用拷贝构造和拷贝赋值(std::thread不可拷贝)
    Wait(const Wait&) = delete;
    Wait& operator=(const Wait&) = delete;

    // 允许移动构造和移动赋值
    Wait(Wait&&) = default;
    Wait& operator=(Wait&&) = default;
};

class Holder {
private:
    std::vector<Wait> waitlist;

public:
    unsigned short addTask(unsigned int waitFor)
    {
        unsigned int sz = waitlist.size();
        waitlist.emplace_back(waitFor, sz, std::bind(&Holder::callback, this, std::placeholders::_1));
        return waitlist.size() - 1;
    }

    bool isDone(unsigned int n) 
    {
        if (n < waitlist.size()) {
            return waitlist.at(n).isDone();
        } 
        else return false;
    }

    void callback(unsigned int id)
    {
        std::cout << "All done with Wait object" << std::endl;
        
        auto it = waitlist.begin() + id;
        waitlist.erase(it); // 现在可以安全移除对象
    }
};

int main() 
{
    Holder h;
    unsigned int id1 = h.addTask(3);
    
    for (auto c = 0; c < 5; c++)
    {
        std::this_thread::sleep_for(Seconds(1));
        std::cout << "from main: " << std::this_thread::get_id() << " - " << h.isDone(id1) << std::endl;
    }

    while(1) {
        std::this_thread::sleep_for(Seconds(1));
        std::cout << "Doing other stuff.." << std::endl;
    }

    return 0;
}

验证结果

修改后运行代码,会得到预期输出:

new Wait objet needs to wait for: 3 seconds
from main: 140234566031168 - 0
from main: 140234566031168 - 0
from thread: 140234566026816
All done with Wait object
from main: 140234566031168 - 1
from main: 140234566031168 - 1
from main: 140234566031168 - 1
Doing other stuff..
Doing other stuff..
Doing other stuff..
Doing other stuff..
Doing other stuff..

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:14:55