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; ?>
】
相关推荐
- php内存溢出怎么排查_php内存限制调试与优化方
- 如何在Golang中实现邮件发送功能_Golang
- Win11如何设置开机问候语 Win11修改登录界
- Win10怎么卸载鲁大师_Win10彻底卸载鲁大师
- Win11怎么关闭自动调节屏幕亮度_Windows
- 如何使用Golang table-driven f
- Win11怎么设置ip地址_Windows 11手
- Python与Docker容器化部署实战_镜像构建
- Windows10如何查看保存的WiFi密码_Wi
- php8.4如何调用com组件_php8.4win
- Python与MongoDB NoSQL开发实战_
- Win11怎么开启游戏工具栏_Windows11
- C++中的Pimpl idiom是什么,有什么好处
- Python函数接口文档化_自动化说明【指导】
- 电脑的“网络和共享中心”去哪了_Windows 1
- php打包exe如何加密代码_防反编译保护方法【技
- php订单日志怎么记录评价_php记录订单评价日志
- C++如何编写函数模板?(泛型编程入门)
- Python对象生命周期管理_创建销毁说明【指导】
- Win11怎么查看激活状态_查询Windows 1
- Win11怎么关闭自动调节亮度 Win11禁用内容
- Python生成器表达式内存优化_惰性计算说明【指
- MAC如何启用访达侧边栏显示_MAC Finder
- 如何使用Golang实现错误包装与传递_Golan
- Windows10系统怎么查看CPU核心数_Win
- MySQL 中使用 IF 和 CASE 实现查询字
- Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡
- Win11怎么关闭开机声音_Win11系统启动提示
- C#如何使用Channel C#通道实现异步通信
- Mac如何与安卓手机传文件_Mac和Android
- Win11怎么开启窗口对齐助手_Windows11
- 如何在Golang中捕获结构体方法错误_Golan
- Win11怎么用设置清理回收站_Win11设置清理
- 如何使用Golang搭建本地API测试环境_快速验
- PHP cURL GET请求:正确设置认证与自定义
- Win11如何设置自动关机 Win11定时关机命令
- Win11怎么设置虚拟桌面 Win11新建多桌面切
- PythonFastAPI项目实战教程_API接口
- Windows系统文件被保护机制阻止怎么办_权限不
- php文件怎么变mp4保存_php输出视频流保存为
- Windows驱动无法加载错误解决方法_驱动签名验
- PythonPandas数据分析教程_数据清洗与处
- 如何在Golang中配置代码格式化工具_使用gof
- Avalonia如何实现跨窗口通信 Avaloni
- Python文件管理规范_工程实践说明【指导】
- 如何优化Golang Web性能_Golang H
- Dapper的Execute方法的返回值是什么意思
- Win10如何卸载预装Edge扩展_Win10卸载
- 如何使用正则表达式批量替换重复的星号-短横模式为固
- Win11怎么关闭应用权限_Windows11相机


QQ客服