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
相关产品推荐
相关产品推荐

