为何连续无延迟启动自定义C++ Timer类时第二次不执行?
问题分析与解决方案
你的Timer类存在几处线程安全相关的问题,导致连续无延迟调用start()时第二次定时器无法正常触发:
核心问题原因
interval_非线程安全:interval_是普通int类型,既不是原子变量,读取时也没有通过互斥锁保护。连续调用start()时,新启动的run线程可能读取到旧的interval_值,或者因编译器优化读取缓存值,导致定时器唤醒时间不符合预期。wait_for_thread_finish()逻辑冗余且存在潜在风险:原代码中先调用wait_for(0)判断线程状态再决定是否wait(),但实际上直接调用timerFuture.wait()即可确保线程结束,多余的判断可能因线程调度时序问题导致未正确等待前线程退出。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; }
关键修改说明
interval_改为原子变量:确保interval_的读写操作都是线程安全的,避免数据竞争和编译器优化带来的旧值读取问题。- 简化
wait_for_thread_finish():直接调用timerFuture.wait(),确保前一个定时器线程完全退出后再启动新线程,避免时序问题。 run()函数内状态读取加锁:将nextWakeUp的计算移到互斥锁保护范围内,确保读取到最新的interval_和状态值。- 统一状态修改逻辑:所有
running的修改都通过互斥锁保护,包括异常处理分支,确保状态一致性。
修改后,连续无延迟调用start()时,第二次定时器会正确读取新的间隔值,按预期触发回调。
内容的提问来源于stack exchange,提问作者sleepyhead
相关产品推荐
相关产品推荐

