std::chrono与std::atomic无法满足计时器原子状态管理需求,因其缺乏对“起始时间+运行状态+暂停标记”组合操作的原子性保障,易致状态撕裂;应采用std::mutex保护三字段状态机(m_start、m_accumulated、m_is_running),所有接口统一加锁,避免锁内阻塞,返回std::chrono::duration以保精度,使用steady_clock,并确保对象生命周期可控。

为什么 std::chrono + std::atomic 不够用
因为计时模块通常要支持「启动/暂停/重置/读取耗时」,而 std::atomic 只能原子地存取一个值,没法原子地组合「记录起始时间 + 当前状态 + 是否暂停中」这些逻辑。直接裸用 std::chrono::steady_clock::now() 在多线程里读写共享变量,会导致状态撕裂:比如线程 A 正在暂停,线程 B 同时调用 elapsed(),可能读到起始时间是旧的、暂停标记却是新的,结果算出负数或巨大偏差。
真正需要的是状态机 + 同步原语,而不是单纯给一个 std::atomic<long long></long> 塞毫秒数。
用 std::mutex 保护状态机比读写锁更稳
有人想用 std::shared_mutex(C++17)做读多写少优化,但计时器的 start()、pause()、reset() 都是写操作,且必须互斥——不能两个线程同时调用 start(),也不能一个在 pause()、另一个在 elapsed() 里修改内部状态。这时 std::shared_mutex 不仅没收益,反而因升级锁(try_lock_shared → lock)引入死锁风险。
实操建议:
- 所有公开接口(
start()、pause()、reset()、elapsed())都用同一个std::mutex加锁,粒度控制在函数入口 - 内部状态只存三个字段:
std::chrono::time_pointm_start、std::chrono::nanosecondsm_accumulated、boolm_is_running - 避免在锁内调用用户回调或可能阻塞的操作(比如日志打印),否则会拖慢所有其他线程对计时器的访问
elapsed() 返回 duration 而不是 double 秒
返回 double 看似方便,但会丢失精度(nanosecond 级别累加后转 double 可能四舍五入掉几十纳秒),也强制用户做类型转换。C++ 标准库的 std::chrono::duration 是零开销抽象,支持直接除法换算、比较、甚至和 std::this_thread::sleep_for() 互通。
示例关键片段:
template <typename rep="long" long typename period="std::nano">
std::chrono::duration<rep period> elapsed() const {
std::lock_guard<:mutex> lk(m_mutex);
auto now = std::chrono::steady_clock::now();
auto delta = m_is_running
? now - m_start
: std::chrono::steady_clock::duration::zero();
return std::chrono::duration_cast<:chrono::duration period>>(
m_accumulated + delta
);
}</:chrono::duration></:mutex></rep></typename>
注意:必须用 steady_clock,不用 system_clock —— 后者可能被系统时间调整干扰;也别用 high_resolution_clock,它在某些平台只是 system_clock 的别名。
构造函数不启动,析构不自动报告
很多新手喜欢在构造时自动 start(),或在析构时打印耗时,这违反单一职责,也容易引发未定义行为(比如对象在静态析构期被调用,此时 std::mutex 可能已销毁)。计时器应保持被动:由使用者明确控制生命周期和时机。
典型误用:
- 把计时器作为全局变量或静态局部变量,导致多线程首次访问时触发静态初始化竞争
- 在 lambda 捕获计时器后,在另一线程里调用
elapsed(),但忘了该 lambda 生命周期是否长于计时器对象 - 用
std::unique_ptr<timer></timer>管理,却在reset()后继续调用成员函数(此时m_mutex已被销毁)
最安全的做法:让计时器是栈对象,作用域清晰;若需跨线程共享,用 std::shared_ptr<timer></timer>,并确保所有访问都通过该指针完成。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











