0

0

C++如何实现基于协程的轻量级定时器调度器?(高并发时间管理)

冰火之心

冰火之心

发布时间:2026-03-08 14:30:14

|

196人浏览过

|

来源于php中文网

原创

不用std::chrono+std::thread直接sleep,因1000个定时器将创建1000线程,导致栈内存与调度开销压垮系统;协程用于避免线程膨胀,需单线程内可挂起/唤醒的定时任务单元。

c++如何实现基于协程的轻量级定时器调度器?(高并发时间管理)

为什么不用 std::chrono + std::thread 直接 sleep?

因为每个定时任务起一个线程,1000 个定时器就 1000 个线程,栈内存、上下文切换、调度开销直接压垮系统。协程不是为“替代线程”而生,是为“避免线程膨胀”而用——尤其在时间轮/最小堆驱动的调度场景里,你真正需要的是:单线程内可挂起、可唤醒、带状态的定时任务单元。

常见错误现象:std::this_thread::sleep_for 在协程里调用会阻塞整个协程执行器(比如 io_context),而不是只挂起当前协程;用 co_await std::chrono::seconds(5) 则根本编译不过(标准库没提供 awaiter)。

  • 必须自己实现或借助第三方 awaitable 类型,如 asio::steady_timerasync_wait 成员返回的可等待对象
  • 底层依赖事件循环(如 boost::asio::io_context 或 C++20 的 std::execution 兼容调度器),不能裸写协程函数就完事
  • 时间精度受系统时钟和调度器 tick 间隔影响,Linux 上 CLOCK_MONOTONIC 是底线,别信 gettimeofday

asio::steady_timer 怎么配合协程做低开销调度?

它不是“定时器对象”,而是“可等待的超时原语”——构造时不启动,async_wait 才注册到 io_context,且返回一个符合 awaitable 要求的代理对象,天然适配 co_await

使用场景:高频短周期心跳(如 100ms 检查连接)、延迟投递消息(如 5s 后重试)、TTL 清理(如 session 30s 过期)。

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

  • 每次 co_await timer.async_wait() 后,必须重新调用 timer.expires_after(...),否则第二次 await 会立即完成(因为过期时间仍是上次设置的)
  • 不要复用同一个 asio::steady_timer 对象跨多个协程并发 await——内部状态非线程安全,应每个协程持有一个独立实例
  • 如果任务逻辑耗时长于定时周期(比如定时每 200ms 执行,但函数跑了 300ms),需在 await 前加判断:if (timer.expiry() ,否则会堆积

简短示例:

Huawei LiteOS物联网操作系统
Huawei LiteOS物联网操作系统

Huawei LiteOS是华为面向物联网领域开发的一个基于实时内核的轻量级操作系统。本项目属于华为物联网操作系统Huawei LiteOS源码,现有基础内核支持任务管理、内存管理、时间管理、通信机制、中断管理、队列管理、事件管理、定时器等操作系统基础组件,更好地支持低功耗场景,支持tickless机制,支持定时器对齐。 同时提供端云协同能力,集成了LwM2M、CoAP、mbedtls、LwIP全

下载
auto do_scheduled_task(asio::steady_timer& timer) -> asio::awaitable<void> {
  while (true) {
    timer.expires_after(200ms);
    co_await timer.async_wait();
    // … 实际工作,注意别超时
  }
}

自己实现最小堆时间轮,协程怎么挂起不卡死主线程?

时间轮或最小堆本身只是数据结构,真正让协程“按时醒来”的,是事件循环对超时事件的轮询与唤醒机制。你不能让协程自己 while 循环检查时间,那等于忙等;也不能让它 co_yield 后靠外部信号唤醒——得把“何时唤醒”这个决策权交给调度器。

关键点在于:协程挂起时,必须把自身句柄(比如一个 std::coroutine_handle)注册进时间轮的对应槽位,并绑定一个“到期回调”,该回调负责恢复协程执行。

  • 回调里不能直接 handle.resume(),要通过调度器 post(如 io_context.post([&] { handle.resume(); })),否则可能破坏线程安全或重入逻辑
  • 协程销毁前必须从时间轮中移除,否则到期时 resume 已释放栈,必崩。建议用 RAII 封装:struct timer_guard { ~timer_guard() { wheel.cancel(handle); } }
  • 最小堆 pop 最小节点后,要立刻检查堆顶是否已超时,而不是固定 sleep 1ms——高精度调度下,这个“检查间隔”就是误差上限

协程定时器在 ASIO 和 libunifex 下的行为差异

ASIO 的 steady_timer::async_wait 返回的是一个 awaitable,但它背后强依赖 io_context 的运行;libunifex 的 schedule_after 则更偏向纯函数式组合,可以脱离具体 executor 存在,但落地仍需绑定调度器。

性能影响明显:ASIO 默认用红黑树管理定时器,O(log n) 插入/删除;libunifex 的 schedule_after 在某些 executor 实现里用的是分层时间轮,O(1) 插入,但内存占用略高。

  • ASIO 中若 io_context::run() 未启动,所有 co_await timer.async_wait() 都不会触发,也不会报错,只是永远挂起
  • libunifex 的 schedule_after(5s) | then([]{...}) | sync_wait() 看似简单,但 sync_wait 会阻塞当前线程,协程优势全丢——必须搭配 schedule + start_detached 或自定义 executor
  • 两者都不支持“动态修改已注册定时器的到期时间”,只能 cancel 再 add,这点容易被忽略

复杂点从来不在协程语法,而在时间语义和生命周期对齐:什么时候注册、谁负责取消、超时回调在哪条线程执行、协程栈和定时器对象谁先析构——这些地方一错,就是 core dump 或静默丢失定时任务。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

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

846

2023.08.22

session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

334

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

775

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

97

2025.08.19

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

435

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

601

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

435

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

601

2023.08.10

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

46

2026.03.06

热门下载

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

精品课程

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

共94课时 | 10.9万人学习

C 教程
C 教程

共75课时 | 5.3万人学习

C++教程
C++教程

共115课时 | 21万人学习

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

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