c++中如何实现简单计时器_c++ timer计时类封装实例【汇总】
技术百科
穿越時空
发布时间:2026-01-22
浏览: 次 标准C++无跨平台Timer类,推荐用std::thread+std::condition_variable实现可取消一次性定时器;周期性触发应基于主循环+std::chrono::steady_clock手动判断,避免阻塞和精度陷阱。
标准 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::alarm 或 setitimer(Linux)做通用计时器
这些是信号驱动的底层接口,SIGALRM 不能安全调用大多数 C++ 对象(如 std::cout、std::vector::push_back),且信号处理函数中禁止 malloc/new、锁、IO 等操作。
使用场景仅限极简嵌入式或信号处理专用模块;现代 C++ 项目中基本被弃用。
-
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::func
tion 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 或内核模块。
# ai
# 操作系统
# 这是
# 要把
# 多个
# windows
# 计时器
# 多久
# win
# ui
# linux
# 循环
# 对象
# c++
# if
# 接口
# 线程
# signal
# 异步
# 信号处理
# 回调
# bug
# 多线程
# 封装
# 成员函数
# while
# Thread
# 可取消
# bool
# 主线程
# 最易
# qt
# 杀鸡
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- php485返回空数组怎么回事_php485数据接
- php怎么操作Redis_Redis扩展连接与基本
- 如何在Windows上设置闹钟和计时器_系统自带的
- Python lxml的etree和Element
- Mac怎么进行语音输入_Mac听写功能设置与使用【
- Windows蓝屏错误0x00000023怎么修复
- Win11怎么关闭小组件_Win11禁用任务栏天气
- Win11怎么开启专注模式_Windows11时钟
- Win11屏幕亮度突然变暗怎么解决_自动变暗问题处
- Win11怎么关闭资讯和兴趣_Windows11任
- 如何使用Golang实现路由参数绑定_使用Mux和
- Golang如何遍历目录文件_Golang fil
- 如何使用Golang实现微服务状态监控_Golan
- 短链接怎么用php递归还原_多层加密链接的处理法【
- Django 测试数据库表缺失与字段未创建问题的完
- Win11怎么关闭定位服务_保护Win11位置隐私
- Python大型项目拆分策略_模块化解析【教程】
- 如何高效删除 NumPy 二维数组中所有元素相同的
- 如何在Golang中处理模块包路径变化_Golan
- 为什么Go建议使用error接口作为错误返回_Go
- XAMPP 启动失败(Apache 突然停止)的终
- Win11怎么设置ipv4地址_Windows 1
- c++ stringstream用法详解_c++字
- 如何使用Golang配置安全开发环境_防止敏感信息
- Win11任务栏颜色怎么改_Win11自定义任务栏
- Win11怎么设置麦克风权限_允许应用访问Win1
- Win11如何更改用户账户文件夹名称 Win11修
- mac怎么右键_MAC鼠标右键设置与触控板手势技巧
- 如何在 Go 应用中实现自动错误恢复与进程重启机制
- 如何在Golang中实现基础配置管理功能_Gola
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- 如何高效获取循环末次生成的 NumPy 数组最后一
- Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡
- Mac上的iMovie如何剪辑视频?(新手入门教程
- 如何在 Go 中正确反序列化多个同级 XML 元素
- Windows服务无法启动错误1067是什么_进程
- Windows11如何设置专注助手_Windows
- Go 语言标准库为何不提供泛型 Contains
- c++怎么使用std::tuple存储多元组数据_
- windows如何禁用驱动程序强制签名_windo
- 微信里的php文件怎么变mp4_微信接收php转m
- Win11怎么设置默认PDF阅读器 Win11修改
- php打包exe后无法写入文件_权限问题解决方法【
- c# Task.Yield 的作用是什么 它和Ta
- Windows10电脑怎么设置文件权限_Win10
- Win11怎么设置虚拟桌面 Win11新建多桌面切
- Python大文件处理策略_内存优化说明【指导】
- 如何使用Golang table-driven f
- php转mp4怎么保留字幕_php处理带字幕视频转
- Python对象生命周期管理_创建销毁解析【教程】


QQ客服