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

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_标志位的状态残留:

  1. 调用DeleteTimer(4)时,对应Timer对象的thread_未初始化(不可joinable),Stop()函数直接将stop_waiting_设为true,但此时没有线程在等待条件变量,notify_one()无实际作用。
  2. 后续调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:12:13