跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何在C++中实现一个轻量级的定时器?(回调函数)

不能直接当定时器用,它只是时间工具;需自行实现循环、超时判断和回调调度,否则易出现精度偏差、线程不安全、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。 C知道 CSDN推出的一款AI技术问答工具 下载 典型错误写法:
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
),提升系统计时精度到 1ms(注意:影响全局,退出前配对调
timeEndPeriod(1)
) Linux/macOS 不需要这步,
nanosleep
clock_nanosleep
默认支持 sub-ms 精度 若只需“大约”精度(如心跳包 30s ± 1s),干脆别纠结 1ms,用
sleep_for(30s)
更稳 复杂点在于:跨平台时,精度控制、线程唤醒机制、回调执行上下文都得隔离。轻量 ≠ 简单,真正可靠的定时器哪怕只有 200 行,也得把 clock、queue、lifecycle、cancel 逻辑全兜住。

相关文章