创建异步Timer类后CreateTimer函数无法返回问题排查
异步Timer类卡住问题排查与修复
我实现了一个可后台运行、自带独立线程的异步Timer类,通过CreateTimer和Delete_Timer函数管理Timer对象,意图支持多线程调用。但运行代码时,CreateTimer函数在打印“returning....”后卡住无法返回,无法排查原因。
原代码如下:
#include <condition_variable> #include <chrono> #include <vector> #include <algorithm> #include <iostream> #include <thread> #include <atomic> typedef struct { uint32_t msgId; uint32_t param1; uint32_t param2; }THRD_EVENT_T; enum THRD_EVENTID_T { THRD_MSGID_TIMER_FIRST, THRD_STATEA, THRD_STATEB, THRD_STATEC, THRD_MSGID_TIMER_LAST }; std::mutex m; void func_(THRD_EVENTID_T timerId_, int32_t pipeId_ ) { std::cout<<"done\n"; } class Timer { public: Timer(THRD_EVENTID_T timerId, int32_t pipeId, uint32_t msPeriod) : timerId_(timerId) , pipeId_(pipeId) , msPeriod_(msPeriod) {} Timer(const Timer &obj) // copy constructor { timerId_ = obj.timerId_; pipeId_ = obj.pipeId_; msPeriod_ = obj.msPeriod_; } Timer() = delete; Timer(Timer&&) = default; ~Timer() { if (thread_.joinable()) thread_.join(); } void start() { std::cout<<"starting the timer\n"; thread_ = std::thread { [this]() { std::unique_lock<std::mutex> lck(mutex_); cv_.wait_for(lck, msPeriod_, [this]() { return stop_waiting_.load(); }); if (not stop_waiting_) func_(timerId_, pipeId_); } }; } void stop() { stop_waiting_.store(true); std::cout<<"Stopping the timer\n"; cv_.notify_one(); } THRD_EVENTID_T GetTimerID() { return timerId_; } private: std::chrono::milliseconds msPeriod_; THRD_EVENTID_T timerId_; int32_t pipeId_; std::thread thread_; std::condition_variable cv_; std::mutex mutex_; std::atomic<bool> stop_waiting_{false}; }; std::vector<Timer> tmrclass; int32_t CreateTimer( const THRD_EVENTID_T timerId, const int32_t pipeId, const uint32_t msPeriod ) { std::cout<<"Creating the timer\n"; std::unique_lock<std::mutex> lock(m); Timer c (timerId, pipeId, msPeriod); tmrclass.emplace_back(c); c.start(); std::cout<<"exiting the createtimer function\n"; std::cout<<"returning....\n"; return 0; } int32_t Delete_Timer( const THRD_EVENTID_T timerId) { int32_t ret = -1; std::cout<<"Deleting the timer function\n"; std::unique_lock<std::mutex> lock(m); for (auto &f : tmrclass) { if(f.GetTimerID() == timerId) { std::cout<<"found the timer to delete\n"; f.stop(); break; } else { ret = -1; } } return ret; } int main(int argc, const char *argv[]) { THRD_EVENTID_T timerId; timerId = THRD_STATEA; int32_t pipeId = 0; uint32_t msPeriod = 5000; CreateTimer(timerId, pipeId, msPeriod); std::cout<<"Have I exited the resource\n"; THRD_EVENTID_T timerId2; timerId2 = THRD_STATEB; int32_t pipeId2 = 1; uint32_t msPeriod2 = 1000; CreateTimer(timerId2, pipeId2, msPeriod2); Delete_Timer(timerId); return 0; }
问题根源
- 拷贝构造函数的致命缺陷:
std::thread是不可拷贝的对象,但你手动实现的拷贝构造函数完全忽略了thread_成员,只复制了基础字段。当CreateTimer里的Timer c被拷贝进tmrclass时,拷贝出的新Timer对象的thread_是空的,而你调用的是c.start()——也就是原对象的线程。当CreateTimer函数执行完毕,局部变量c被销毁,触发~Timer(),里面的thread_.join()会等待这个正在运行的线程结束,这就是程序卡住的原因。 - vector存储方式错误:直接存储
Timer对象会触发拷贝操作,而你的Timer类设计没有考虑线程的所有权转移,导致线程管理混乱。
修复方案
关键修改点
- 禁用拷贝操作:删除拷贝构造和拷贝赋值运算符,因为
std::thread不可拷贝,避免意外拷贝导致的未定义行为。 - 改用智能指针存储:用
std::vector<std::unique_ptr<Timer>>替代std::vector<Timer>,通过智能指针管理Timer对象的所有权,避免拷贝。 - 调整线程启动时机:先启动线程再将智能指针放入vector,确保线程所有权正确转移。
- 优化析构和停止逻辑:在析构函数中先调用
stop(),确保线程被正确唤醒后再join,避免不必要的阻塞。
修复后的完整代码
#include <condition_variable> #include <chrono> #include <vector> #include <algorithm> #include <iostream> #include <thread> #include <atomic> #include <memory> typedef struct { uint32_t msgId; uint32_t param1; uint32_t param2; }THRD_EVENT_T; enum THRD_EVENTID_T { THRD_MSGID_TIMER_FIRST, THRD_STATEA, THRD_STATEB, THRD_STATEC, THRD_MSGID_TIMER_LAST }; std::mutex m; void func_(THRD_EVENTID_T timerId_, int32_t pipeId_ ) { std::cout<<"done\n"; } class Timer { public: Timer(THRD_EVENTID_T timerId, int32_t pipeId, uint32_t msPeriod) : timerId_(timerId) , pipeId_(pipeId) , msPeriod_(msPeriod) {} // 禁用拷贝构造和拷贝赋值 Timer(const Timer &obj) = delete; Timer& operator=(const Timer&) = delete; // 默认移动构造和移动赋值 Timer(Timer&&) = default; Timer& operator=(Timer&&) = default; ~Timer() { stop(); if (thread_.joinable()) thread_.join(); } void start() { std::cout<<"starting the timer\n"; // 确保线程未重复启动 if (!thread_.joinable()) { thread_ = std::thread{ [this]() { std::unique_lock<std::mutex> lck(mutex_); cv_.wait_for(lck, msPeriod_, [this]() { return stop_waiting_.load(); }); if (!stop_waiting_) func_(timerId_, pipeId_); }}; } } void stop() { stop_waiting_.store(true); cv_.notify_one(); } THRD_EVENTID_T GetTimerID() { return timerId_; } private: std::chrono::milliseconds msPeriod_; THRD_EVENTID_T timerId_; int32_t pipeId_; std::thread thread_; std::condition_variable cv_; std::mutex mutex_; std::atomic<bool> stop_waiting_{false}; }; std::vector<std::unique_ptr<Timer>> tmrclass; int32_t CreateTimer( const THRD_EVENTID_T timerId, const int32_t pipeId, const uint32_t msPeriod ) { std::cout<<"Creating the timer\n"; std::unique_lock<std::mutex> lock(m); auto timer_ptr = std::make_unique<Timer>(timerId, pipeId, msPeriod); timer_ptr->start(); tmrclass.emplace_back(std::move(timer_ptr)); std::cout<<"exiting the createtimer function\n"; std::cout<<"returning....\n"; return 0; } int32_t Delete_Timer( const THRD_EVENTID_T timerId) { int32_t ret = -1; std::cout<<"Deleting the timer function\n"; std::unique_lock<std::mutex> lock(m); // 用find_if高效查找目标Timer auto it = std::find_if(tmrclass.begin(), tmrclass.end(), [timerId](const std::unique_ptr<Timer>& t) { return t->GetTimerID() == timerId; }); if (it != tmrclass.end()) { std::cout<<"found the timer to delete\n"; (*it)->stop(); tmrclass.erase(it); // 移除后智能指针自动释放对象 ret = 0; } return ret; } int main(int argc, const char *argv[]) { THRD_EVENTID_T timerId = THRD_STATEA; int32_t pipeId = 0; uint32_t msPeriod = 5000; CreateTimer(timerId, pipeId, msPeriod); std::cout<<"Have I exited the resource\n"; THRD_EVENTID_T timerId2 = THRD_STATEB; int32_t pipeId2 = 1; uint32_t msPeriod2 = 1000; CreateTimer(timerId2, pipeId2, msPeriod2); Delete_Timer(timerId); return 0; }
修复后说明
- 程序不再卡住,
CreateTimer可以正常返回,因为局部变量不再持有运行中的线程,线程的所有权由vector中的智能指针管理。 Delete_Timer现在会直接移除并销毁目标Timer对象,确保资源正确释放。- 禁用拷贝操作后,避免了所有因线程拷贝导致的潜在问题。
内容的提问来源于stack exchange,提问作者newww
相关产品推荐
相关产品推荐

