Asio学习:定时器

核心名词

  • io_context:事件循环/调度器,负责等待事件并执行回调(handler)。统一管理 IO 与定时器回调,便于扩展到网络模型。
  • handler(回调) :事件就绪后被 run() 调用的函数/lambda。
  • async_wait:登记一次"到期后执行 handler"的异步操作;调用后立即返回(不阻塞)。
  • steady_timer:稳定定时器(不受系统时间调整影响)。
  • 多线程 run :多个线程同时调用 ioc.run(),同一时刻可能执行多个 handler。
  • strand :保证绑定到同一 strand 的 handler 不并发执行(串行化),但不保证固定线程。
  • work_guard :防止 run() 在暂时无任务时提前返回;reset() 后允许自然退出。

封装业务对象:Ticker

cpp 复制代码
class Ticker : public std::enable_shared_from_this<Ticker> {
public:
  Ticker(asio::io_context &ico, int limit, std::function<void()> on_done)
      : strand_(asio::make_strand(ico)), timer_(strand_), limit_(limit),
        on_done_(on_done) {}

  void start() {
    // 开始第一次的tick
    schedule_next_tick();
  }
  
private:
  void schedule_next_tick() {
    timer_.expires_after(1s);
    timer_.async_wait(
        [self = shared_from_this()](const boost::system::error_code &ec) {
          if (ec) {
            std::cerr << "timer async wait error: " << ec.message() << "\n";
                    return;
          }
          self->tick();
    });
  }

  void tick() {
    ++count_;
    std::cout << "tick: " << count_
              << " (thread_id=" << std::this_thread::get_id() << ")\n";
    if (count_ >= limit_) {
      if (on_done_)
        on_done_();
      return;
    }
    // 安排下一次tick
    schedule_next_tick();
  }
  
private:
  asio::strand<asio::any_io_executor> strand_;
  asio::steady_timer timer_;
  int count_ = 0;
  int limit_;
  std::function<void()> on_done_;
};
  1. Ticker集成[std::](std::enable_shared_from_this,回调里面捕获 shared_from_this()

为什么这样做?

  • 异步回调可能在未来任意时刻触发
  • 捕获 shared_ptr 可以保证回调执行时对象仍然存在,避免悬空指针
  1. 内部绑定strand_绑定定时器调用
    • 多线程运行 io_context 会导致回调并发执行
    • strand 保证同一对象的回调串行化,避免数据竞争
    • 这样count_ 不需要加锁,更容易推理行为。

thread pool
strand (serial executor)
handler A
handler B
handler C
thread1: ioc.run()
thread2: ioc.run()
thread3: ioc.run()
io_context

ready handlers queue

  1. schedule_next_tick()expires_after(1s) + async_wait,回调中再调用 schedule_next_tick()
    • 把"下一次调度"放在回调里,避免阻塞线程
    • 更容易与 io_context 的事件循环协作

保持事件循环存活(work_guard)

cpp 复制代码
auto guard = asio::make_work_guard(ico);
auto on_done = [&guard]() { guard.reset(); };
  • 创建 executor_work_guard,任务完成后 reset()
    • 若没有未完成异步任务,io_context.run() 会立即返回
    • work_guard 让事件循环持续运行,直到我们主动停止
    • 完成时 reset(),确保线程池可以自然退出

线程池启动

cpp 复制代码
// 线程池
std::vector<std::thread> pool;
pool.reserve(threads);
for (int i = 0; i < threads; ++i) {
  pool.emplace_back([&ico]() {
    ico.run();
  });
}
for (auto &t : pool) {
  t.join();
}

线程与并发模型

  • 线程池数量由 threads 控制
  • io_context 可以被多个线程并发运行
  • 绑定 strand_ 的回调串行执行,避免数据竞争

最小代码骨架

cpp 复制代码
asio::io_context ioc;
auto guard = asio::make_work_guard(ioc);  // 可选但常用

// 1) 创建异步对象(timer/socket等)
// 2) 发起 async_* 操作,提供 handler(回调)
start_async_ops();

// 3) 一个或多个线程跑事件循环
std::thread t([&]{ ioc.run(); });
t.join();

定时器循环的通用写法:

cpp 复制代码
void schedule() {
  timer.expires_after(1s);
  timer.async_wait([this](ec){
    if (!ec) { do_work(); schedule(); }
  });
}
相关推荐
t***54417 小时前
如何配置Orwell Dev-C++使用Clang
开发语言·c++
CoderCodingNo17 小时前
【信奥业余科普】C++ 的奇妙之旅 | 13:为什么 0.1+0.2≠0.3?——解密“爆int”溢出与浮点数精度的底层原理
开发语言·c++
极客智造19 小时前
深入详解 C++ 智能指针:RAII 原理、分类特性、底层机制与工程实战
c++·智能指针
王璐WL20 小时前
【C++】类的默认成员函数(上)
c++
王老师青少年编程20 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【区间贪心】:区间覆盖(加强版)
c++·算法·贪心·csp·信奥赛·区间贪心·区间覆盖(加强版)
宏笋21 小时前
C++11完美转发的作用和用法
c++
格发许可优化管理系统21 小时前
MathCAD许可类型全面解析:选择最适合您的许可证
c++
旖-旎21 小时前
深搜(二叉树的所有路径)(6)
c++·算法·leetcode·深度优先·递归
GIS阵地1 天前
QGIS的分类渲染核心类解析
c++·qgis·开源gis
凯瑟琳.奥古斯特1 天前
C++变量与基本类型精解
开发语言·c++