C++ Timer类:先调用Stop再Start后回调未执行问题排查
定时器未启动前调用Stop后创建回调不执行的原因分析
问题场景
此前曾在Stack Overflow提问《C++ thread exits after calling terminate called without an active exception》并获可行解答。现扩展代码功能时遇到如下问题:主程序有时会在启动定时器前调用Stop函数,尝试在自定义Timer类中处理该场景,但示例代码中先调用DeleteTimer删除slot4的未启动定时器,之后创建它,然而创建后的回调函数并未执行。
示例代码
#include <condition_variable> #include <mutex> #include <chrono> #include <vector> #include <algorithm> #include <iostream> #include <thread> #include <atomic> class Timer { public: Timer(); ~Timer(); void Start(const int32_t slot, const uint8_t pipeId, const uint32_t msPeriod); void Stop(); private: void TimerCallbacktimer(const int32_t slot, const uint8_t pipeId); std::chrono::milliseconds msPeriod_; uint8_t pipeId_; int32_t slot_; std::thread thread_; std::condition_variable cv_; std::mutex mutex_; std::atomic<bool> stop_waiting_{false}; std::atomic<bool> done_{false}; }; void Timer::TimerCallbacktimer(const int32_t slot, const uint8_t pipeId) { std::cout<<"the timer callback for slot: "<<slot<<std::endl; } void Timer::Start(const int32_t slot, const uint8_t pipeId, const uint32_t msPeriod) { pipeId_ = pipeId; slot_ = slot; msPeriod_ = std::chrono::milliseconds(msPeriod); if (thread_.joinable()) { stop_waiting_.store(false); thread_.join(); done_.store(false); } 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_) { TimerCallbacktimer(slot_, pipeId_ ); done_.store(true); } } }; } void Timer::Stop() { stop_waiting_.store(true); cv_.notify_one(); } Timer::Timer() { } Timer::~Timer() { if (thread_.joinable()) { thread_.join(); } } std::vector<Timer> TimerArray(30); std::mutex timerMutex; void CreateTimer( const int32_t slot , const int32_t pipeId, const uint32_t msPeriod ) { std::unique_lock<std::mutex> lock(timerMutex); TimerArray[slot].Start(slot, pipeId, msPeriod); } void DeleteTimer( const int32_t slot ) { std::unique_lock<std::mutex> lock(timerMutex); TimerArray[slot].Stop(); } int main(int argc, char const *argv[]) { // sometimes a delete timer may be called before actually starting the timer DeleteTimer(4); CreateTimer(3, 3, 80); CreateTimer(4, 3, 10); DeleteTimer(10); CreateTimer(10, 3, 300); CreateTimer(3, 3, 100); // sometimes I start the same timer immediately after it has called its callback function CreateTimer(3, 3, 100); return 0; }
原因分析
问题出在stop_waiting_标志位的状态残留:
- 调用
DeleteTimer(4)时,对应Timer对象的thread_未初始化(不可joinable),Stop()函数直接将stop_waiting_设为true,但此时没有线程在等待条件变量,notify_one()无实际作用。 - 后续调用
CreateTimer(4,3,10)进入Start()函数:- 因为
thread_不可joinable,跳过了if(thread_.joinable())分支,没有重置stop_waiting_。 - 新创建的线程执行
wait_for时,stop_waiting_仍为true,导致wait_for立刻返回,且因stop_waiting_为true,不会进入执行回调的分支,最终回调函数未被调用。
- 因为
解决方案
有两种可行的修复方式:
方式一:启动定时器前强制重置标志位
在Start()函数开头添加stop_waiting_.store(false);,确保每次启动定时器时标志位处于初始状态:
void Timer::Start(const int32_t slot, const uint8_t pipeId, const uint32_t msPeriod) { stop_waiting_.store(false); // 重置标志位,避免之前的Stop操作残留 pipeId_ = pipeId; slot_ = slot; msPeriod_ = std::chrono::milliseconds(msPeriod); if (thread_.joinable()) { thread_.join(); done_.store(false); } 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_) { TimerCallbacktimer(slot_, pipeId_ ); done_.store(true); } } }; }
方式二:仅当线程存在时修改标志位
修改Stop()函数,仅在thread_可joinable(即定时器已启动)时才设置stop_waiting_并通知条件变量:
void Timer::Stop() { if (thread_.joinable()) { // 只有定时器已启动时才执行停止操作 stop_waiting_.store(true); cv_.notify_one(); } }
两种方式都能解决未启动前调用Stop导致后续回调不执行的问题,可根据实际需求选择。
内容的提问来源于stack exchange,提问作者newww
相关产品推荐
相关产品推荐

