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

为何连续无延迟启动自定义C++ Timer类时第二次不执行?

问题分析与解决方案

你的Timer类存在几处线程安全相关的问题,导致连续无延迟调用start()时第二次定时器无法正常触发:

核心问题原因

  1. interval_非线程安全:interval_是普通int类型,既不是原子变量,读取时也没有通过互斥锁保护。连续调用start()时,新启动的run线程可能读取到旧的interval_值,或者因编译器优化读取缓存值,导致定时器唤醒时间不符合预期。
  2. wait_for_thread_finish()逻辑冗余且存在潜在风险:原代码中先调用wait_for(0)判断线程状态再决定是否wait(),但实际上直接调用timerFuture.wait()即可确保线程结束,多余的判断可能因线程调度时序问题导致未正确等待前线程退出。
  3. run()函数中状态修改不一致:函数末尾直接修改running = false,未通过互斥锁保护,虽然running是原子变量,但与其他状态修改逻辑不一致,可能导致状态混乱。

修复后的代码

#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>
#include <future>
#include <mutex>
#include <condition_variable>

class Timer {
public:
    Timer() : running(false), stopRequested(false), interval_(0), singleShot(false) {}

    void start(std::chrono::milliseconds interval) {
        stop();
        wait_for_thread_finish();

        {
            std::lock_guard<std::mutex> lock(mutex);
            interval_ = static_cast<int>(interval.count());
            running = true;
            stopRequested = false;
        }

        timerFuture = std::async(std::launch::async, &Timer::run, this);
    }

    void stop() {
        {
            std::lock_guard<std::mutex> lock(mutex);
            stopRequested = true;
            running = false;
        }
        cv.notify_all();
    }

    void wait_for_thread_finish() {
        if (timerFuture.valid()) {
            timerFuture.wait(); // 直接等待线程结束,简化逻辑
        }
    }

    void run() {
        try {
            while (true) {
                std::chrono::milliseconds currentInterval;
                std::chrono::steady_clock::time_point nextWakeUp;
                {
                    std::unique_lock<std::mutex> lock(mutex);
                    if (stopRequested || !running) break;
                    currentInterval = std::chrono::milliseconds(interval_);
                    nextWakeUp = std::chrono::steady_clock::now() + currentInterval;
                }

                {
                    std::unique_lock<std::mutex> lock(mutex);
                    if (cv.wait_until(lock, nextWakeUp, [this] { return stopRequested.load(); }))
                        break;

                    if (!running.load()) break;
                }

                timeoutCallback();

                if (singleShot.load()) {
                    std::lock_guard<std::mutex> lock(mutex);
                    running = false;
                    break;
                }
            }

            {
                std::lock_guard<std::mutex> lock(mutex);
                running = false;
            }
        }
        catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
            std::lock_guard<std::mutex> lock(mutex);
            running = false;
        }
    }

    void timeoutCallback() {
        std::cout << "Timer triggered!" << std::endl;
    }

private:
    std::atomic<bool> running;
    std::atomic<bool> stopRequested;
    std::atomic<int> interval_; // 修改为原子变量,确保读写线程安全
    std::atomic<bool> singleShot;

    std::mutex mutex;
    std::condition_variable cv;
    std::future<void> timerFuture;
};

int main() {
    Timer timer1;

    std::cout << "Starting timer 1 immediately...\n";
    
    timer1.start(std::chrono::milliseconds(1000));
    timer1.start(std::chrono::milliseconds(1099));
    
    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout << "Stopping timers...\n";
    timer1.stop();

    timer1.wait_for_thread_finish();

    std::cout << "Main thread exiting...\n";
    return 0;
}

关键修改说明

  1. interval_改为原子变量:确保interval_的读写操作都是线程安全的,避免数据竞争和编译器优化带来的旧值读取问题。
  2. 简化wait_for_thread_finish():直接调用timerFuture.wait(),确保前一个定时器线程完全退出后再启动新线程,避免时序问题。
  3. run()函数内状态读取加锁:将nextWakeUp的计算移到互斥锁保护范围内,确保读取到最新的interval_和状态值。
  4. 统一状态修改逻辑:所有running的修改都通过互斥锁保护,包括异常处理分支,确保状态一致性。

修改后,连续无延迟调用start()时,第二次定时器会正确读取新的间隔值,按预期触发回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:04:51