不能直接当定时器用,它只是时间工具;需自行实现循环、超时判断和回调调度,否则易出现精度偏差、线程不安全、ui崩溃、资源耗尽及悬垂引用等严重问题。

std::chrono + std::thread 能否直接做定时器?
不能直接当「定时器」用,它只是时间工具。你得自己搭循环、判断超时、调回调——容易漏掉精度控制和线程安全问题。
常见错误现象:std::this_thread::sleep_for 睡太久或太短,尤其在系统负载高时偏差可达几十毫秒;回调在子线程执行,但用户代码假设在主线程里改 UI 或访问 std::vector,结果崩得无声无息。
- 别用
while (true)+sleep_for做长周期定时(比如 5 分钟),系统休眠或调度延迟会让下次触发严重偏移 - 如果回调要访问共享数据,必须加锁,或改用
std::atomic保护简单标志位 - 优先用
std::chrono::steady_clock,不用system_clock——后者可能被 NTP 调整,导致“时间倒流”误判超时
如何避免重复创建线程开销?
每次 setTimer 都 new 一个 std::thread,短周期高频定时(如 10ms)会迅速耗尽线程资源,甚至触发 std::system_error: Resource temporarily unavailable。
正确做法是复用单个后台线程,靠队列驱动多个定时任务:
立即学习“C++免费学习笔记(深入)”;
- 用
std::priority_queue存待触发的定时器,按唤醒时间排序,顶部是最先要触发的 - 后台线程只做一件事:等最近那个定时器到期,pop 出来,调回调,再 loop
- 插入新定时器时用
std::condition_variable::notify_one()唤醒等待中的线程,避免死等
示例关键逻辑:
auto now = std::chrono::steady_clock::now();
auto when = now + std::chrono::milliseconds(100);
timer_queue.push({when, []{ /* callback */ }});
cv.notify_one(); // 唤醒空闲线程
std::function 捕获变量后生命周期怎么管?
这是最常踩的坑:回调里用了局部变量的引用或指针,定时器还没触发函数就返回了,回调一执行就 UB。
典型错误写法:
void start_timer() {
int value = 42;
timer.set(1000, [&value]{ printf("%d\n", value); }); // 危险!
}
安全做法只有两种:
- 值捕获全部依赖项:
[value]{ printf("%d\n", value); }(适合小对象、可拷贝) - 用
std::shared_ptr包裹大对象,回调捕获该智能指针:[ptr = shared_from_this()]{ ptr->do_work(); } - 绝对不要捕获栈上容器的迭代器、
std::string::c_str()返回的指针——它们随局部变量销毁而失效
Windows 下 Sleep(1) 为什么不准?
不是 C++ 问题,是 Windows 内核默认时间粒度为 15.6ms。即使你 sleep_for(1ms),实际可能睡 16ms,累积误差极大。
解决办法有限但有效:
- 调一次
timeBeginPeriod(1)(需#include <windows.h></windows.h>),提升系统计时精度到 1ms(注意:影响全局,退出前配对调timeEndPeriod(1)) - Linux/macOS 不需要这步,
nanosleep或clock_nanosleep默认支持 sub-ms 精度 - 若只需“大约”精度(如心跳包 30s ± 1s),干脆别纠结 1ms,用
sleep_for(30s)更稳
复杂点在于:跨平台时,精度控制、线程唤醒机制、回调执行上下文都得隔离。轻量 ≠ 简单,真正可靠的定时器哪怕只有 200 行,也得把 clock、queue、lifecycle、cancel 逻辑全兜住。










