0

0

c++中如何实现简单计时器_c++ timer计时类封装实例【汇总】

穿越時空

穿越時空

发布时间:2026-01-22 11:32:42

|

171人浏览过

|

来源于php中文网

原创

标准C++无跨平台Timer类,推荐用std::thread+std::condition_variable实现可取消一次性定时器;周期性触发应基于主循环+std::chrono::steady_clock手动判断,避免阻塞和精度陷阱。

c++中如何实现简单计时器_c++ timer计时类封装实例【汇总】

标准 C++ 没有内置的、跨平台的 Timer 类,所以“实现一个简单计时器”本质是封装系统级异步/轮询机制。直接用 std::this_thread::sleep_for + 循环是最易错的方式,不推荐用于真实计时逻辑。

std::thread + std::condition_variable 实现可取消的一次性定时器

这是最常用且可控的方案:启动一个分离线程,在指定延迟后通知回调,同时支持中途取消。关键不是“每隔多久执行”,而是“延迟多久后执行一次”。

常见错误现象:std::this_thread::sleep_for 在主线程里阻塞,导致 UI 冻结或无法响应中断;忘记加 std::mutex 保护取消标志,引发数据竞争。

  • 使用场景:网络超时、按钮防抖、延时初始化
  • 必须用 std::atomic 或带锁的 bool 记录 m_cancelled,否则多线程读写未同步会 UB
  • 不要在回调中直接操作 GUI 控件(如 Qt 的 QWidget),应发信号或投递到主线程
  • std::condition_variable::wait_for 比裸 sleep_for 更利于响应取消
class SimpleTimer {
    std::thread m_thread;
    std::atomic m_cancelled{false};
    std::function m_callback;

public:
    explicit SimpleTimer(std::function cb) : m_callback(std::move(cb)) {}

    void start(std::chrono::milliseconds delay) {
        if (m_thread.joinable()) m_thread.join();
        m_cancelled = false;
        m_thread = std::thread([this, delay] {
            std::mutex mtx;
            std::condition_variable cv;
            std::unique_lock lk(mtx);
            cv.wait_for(lk, delay, [this] { return m_cancelled.load(); });
            if (!m_cancelled.load()) m_callback();
        });
        m_thread.detach(); // 或者由使用者管理生命周期
    }

    void cancel() { m_cancelled = true; }
};

避免用 std::alarmsetitimer(Linux)做通用计时器

这些是信号驱动的底层接口,SIGALRM 不能安全调用大多数 C++ 对象(如 std::coutstd::vector::push_back),且信号处理函数中禁止 malloc/new、锁、IO 等操作。

立即学习C++免费学习笔记(深入)”;

使用场景仅限极简嵌入式或信号处理专用模块;现代 C++ 项目中基本被弃用。

Kive
Kive

一站式AI图像生成和管理平台

下载
  • std::signal(SIGALRM, handler) 注册的函数必须是 async-signal-safe 的,C++ 成员函数无法直接注册
  • 多个 SimpleTimer 实例共用同一个 SIGALRM,无法区分来源
  • Windows 下完全不可用,零跨平台性

需要周期性触发?用 std::chrono + 主循环手动判断(非阻塞)

GUI 应用、游戏主循环、嵌入式主干逻辑中,不应让计时器自己开线程,而应在每帧检查是否到期——这是最稳定、最易调试的方式。

性能影响:每次调用 std::chrono::steady_clock::now() 开销极小(通常几纳秒),远低于线程调度或系统调用。

  • 存储下次触发时间点(std::chrono::time_point),而非倒计时剩余毫秒数,避免累积误差
  • if (now >= next_fire) { callback(); next_fire += interval; },别用 while 防止卡顿导致连发
  • 不要把 std::this_thread::sleep_for 放进主循环来“省电”,它会让响应延迟不可控
class PeriodicTimer {
    std::chrono::steady_clock::time_point m_next_fire;
    std::chrono::milliseconds m_interval;
    std::function m_callback;

public:
    PeriodicTimer(std::chrono::milliseconds interval, std::function cb)
        : m_interval(interval), m_callback(std::move(cb)) {
        reset();
    }

    void reset() {
        m_next_fire = std::chrono::steady_clock::now() + m_interval;
    }

    bool update() { // 返回 true 表示本次触发了回调
        auto now = std::chrono::steady_clock::now();
        if (now >= m_next_fire) {
            m_callback();
            m_next_fire += m_interval;
            return true;
        }
        return false;
    }
};

第三方库选型提醒:别为简单需求引入 heavy 依赖

Boost.Asio 的 boost::asio::steady_timer 功能完备,但强制绑定 io_context 和 strand,对单次延时任务属于杀鸡用牛刀;Qt 的 QTimer 依赖整个 Qt 框架,无法用于 headless 服务端。

真正容易被忽略的点:计时精度不等于系统时钟精度。std::chrono::steady_clock 在 Linux 上通常基于 CLOCK_MONOTONIC,但实际唤醒延迟受调度器影响,10ms 定时可能偏差 ±2ms —— 这不是 bug,是操作系统本质限制。硬实时场景必须用 RTOS 或内核模块。

相关专题

更多
视频后缀名都有哪些
视频后缀名都有哪些

视频后缀名都有avi、mpg、mpeg、rm、rmvb、flv、wmv、mov、mkv、ASF、M1V、M2V、MPE、QT、VOB、RA、RMJ、RMS、RAM、等等。更多关于视频后缀名的相关知识,详情请看本专题下面的文章,php中文网欢迎大家前来学习。

3482

2023.10.31

C++ Qt图形开发
C++ Qt图形开发

本专题专注于 C++ Qt框架在图形界面开发中的应用,系统讲解窗口设计、信号与槽机制、界面布局、事件处理、数据库连接与跨平台打包等核心技能,通过多个桌面应用项目实战,帮助学员快速掌握 Qt 框架并独立完成跨平台GUI软件的开发。

68

2025.08.15

C++ 图形界面开发基础(Qt方向)
C++ 图形界面开发基础(Qt方向)

本专题系统讲解 使用 C++ 与 Qt 进行图形界面(GUI)开发的核心技能,内容涵盖 Qt 项目结构、窗口组件、信号与槽机制、事件处理、布局管理、资源管理,以及跨平台编译与打包流程。通过多个小型桌面应用实战案例,帮助学习者掌握从界面设计到功能实现的完整 GUI 开发能力。

58

2025.12.05

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

757

2023.08.22

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

91

2023.09.25

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1049

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

86

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

457

2025.12.29

Golang 性能分析与pprof调优实战
Golang 性能分析与pprof调优实战

本专题系统讲解 Golang 应用的性能分析与调优方法,重点覆盖 pprof 的使用方式,包括 CPU、内存、阻塞与 goroutine 分析,火焰图解读,常见性能瓶颈定位思路,以及在真实项目中进行针对性优化的实践技巧。通过案例讲解,帮助开发者掌握 用数据驱动的方式持续提升 Go 程序性能与稳定性。

5

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.6万人学习

Git 教程
Git 教程

共21课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号