可取消的单次定时器需用std::thread+std::atomic实现:在独立线程中循环检查超时与stop_requested标志,每次sleep_for(1ms)以平衡精度与CPU占用,避免condition_variable虚假唤醒。

用 std::chrono + std::thread 实现可取消的单次定时器
直接用 std::this_thread::sleep_for 等待再执行,是最轻量的单次定时方案,但没法中途取消。如果需要支持「启动后随时停掉」,就得把等待逻辑放进独立线程,并用 std::atomic<bool></bool> 控制退出条件。
- 别用
std::condition_variable::wait_for配虚假唤醒做定时——它本质仍是阻塞等待,且唤醒时机不可靠 - 推荐写法:在新线程里循环检查是否超时 + 是否被请求停止,每次最多
sleep_for(1ms),平衡精度和 CPU 占用 - 注意
std::atomic<bool></bool>必须声明为std::atomic<bool> stop_requested{false}</bool>,不能只靠局部变量捕获进 lambda - 示例关键片段:
std::thread([duration, callback, &stop_requested] { auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - start < duration && !stop_requested.load()) { std::this_thread::sleep_for(1ms); } if (!stop_requested.load()) callback(); });
避免 std::this_thread::sleep_for 在 Windows 上误差超 15ms
Windows 默认时间精度约 15–16ms,sleep_for(1ms) 实际可能卡住 15ms。对精度要求稍高的场景(比如动画帧调度、网络心跳),必须主动提升系统计时器精度。
- 调用
timeBeginPeriod(1)(需链接winmm.lib)可将最小睡眠间隔压到 1ms,但会影响全局电源策略,记得配对调用timeEndPeriod(1) - Linux/macOS 无此问题,
sleep_for基本能逼近纳秒级精度(取决于内核调度) - 如果只是做「大约几秒后执行」,误差 10–20ms 完全可接受,不用折腾系统 API
多个定时任务共用一个线程池?小心 std::function 拷贝开销
想复用线程避免频繁创建销毁,常见做法是维护一个任务队列 + 工作线程。但定时器回调若携带大对象(比如闭包捕获了 std::vector 或 std::shared_ptr),每次入队都会触发深拷贝。
- 用
std::move转移回调:队列存std::function<void>&&</void>,入队时写queue.push(std::move(cb)) - 更彻底的解法:回调只存裸函数指针或轻量句柄,真实数据放外部缓存,用 ID 查找
- 别在定时器回调里做耗时操作(如文件读写、网络请求)——会阻塞整个线程池
std::alarm 和 setitimer 在现代 C++ 项目里基本不该用
这两个 POSIX 接口发的是 SIGALRM 信号,而 C++ 对象析构、STL 容器操作、甚至 std::cout 都不是异步信号安全的。一旦信号打断正在执行的 std::vector::push_back,大概率崩溃。
立即学习“C++免费学习笔记(深入)”;
- 除非你在写纯 C 风格嵌入式程序,且全程不调用任何 C++ 标准库,否则不要碰
signal相关 API - Windows 上根本没
setitimer,跨平台代码直接废掉 - 所有标准库定时方案(
std::thread、std::async、第三方如boost::asio::steady_timer)都基于线程/事件循环,安全可控
实际用起来最麻烦的从来不是「怎么启动一个定时器」,而是「怎么确保它一定被正确销毁」——尤其是回调里持有 this 指针时,对象提前析构而定时器还在跑,野指针就藏在这儿。


















