最稳妥的方式是用 std::function 存储回调,支持 lambda、普通函数及绑定成员函数;捕获需按值或确保引用对象生命周期长于定时器,禁止存裸 this 指针。

回调函数怎么安全绑定到定时器对象上
直接用 std::function 存储回调最稳妥,避免裸函数指针或成员函数指针带来的生命周期和调用语法问题。关键不是“能不能绑”,而是“绑完会不会调用时崩溃”。
- 用
std::function<void></void>作为回调类型,支持 lambda、普通函数、绑定后的成员函数 - lambda 捕获必须按值(
[=])或谨慎按引用(确保捕获对象生命周期长于定时器) - 不要存裸
this指针进回调——如果定时器对象提前析构,回调触发时就是 UBSAN 或段错误 - 示例:
timer.start(1000, [=]() { std::cout
如何避免多线程环境下定时器重复触发或竞态
单线程场景下用 std::thread + std::this_thread::sleep_for 最轻量;但一旦涉及多个定时器或外部事件干预,必须加锁——否则 stop() 可能被正在执行的回调绕过。
- 用
std::atomic<bool></bool>控制运行状态(如m_active),比互斥锁开销小,适合只读检查 - 修改定时器状态(如
start()/stop())必须用std::mutex保护共享字段(如下次触发时间、是否重复) - 回调执行期间不持有锁——否则会阻塞其他定时器操作;把锁范围缩到最小,仅保护状态读写
- 注意:Windows 上
std::this_thread::sleep_for实际精度常为 15ms,Linux 通常更准;对亚毫秒级需求需换std::condition_variable+ 高精度时钟
重复定时与一次性定时的实现差异在哪
核心区别在于每次回调执行后,是否重新计算并设置下一次触发时间。别用两个类或一堆 if 分支,一个布尔字段 + 统一逻辑就能覆盖。
- 用
bool m_repeat标记类型,start(delay_ms, callback, repeat = true)接口统一 - 在执行回调后判断:
if (m_repeat) m_next_trigger = steady_clock::now() + milliseconds(delay_ms); - 一次性定时器触发后自动置
m_active = false,无需手动stop() - 注意:如果回调里调用了
start()自身(比如重置倒计时),要防止递归调用导致栈溢出——可加if (m_is_running) return;防御
为什么 stop() 后还要等当前 sleep 结束才能真正退出
因为 std::this_thread::sleep_for 是阻塞调用,无法中断。你调了 stop(),线程还在睡,只是醒来后不再继续循环。这是设计取舍,不是 bug。
- 想立即退出?得换
std::condition_variable+wait_for,用notify_one()唤醒等待中的线程 - 但代价是代码变复杂、有额外同步开销;对大多数 UI 或后台轮询场景,容忍最多 delay_ms 的延迟更简单可靠
- 真正要注意的是:
stop()必须同时置m_active = false并 notify(如果用了条件变量),否则唤醒后可能又进入下一轮 sleep - 测试时别只验证
stop()调用后是否不再打印,要检查线程是否真正结束——用join()等待,或加日志确认线程函数返回
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











