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(); }
  });
}
相关推荐
陳10302 小时前
C++:哈希表
开发语言·c++·散列表
WBluuue2 小时前
数据结构与算法:01分数规划
c++·算法
行稳方能走远2 小时前
结构体传参,到底该传值还是传指针?
c++·单片机
sycmancia2 小时前
C++——函数模板的概念和意义
c++
闻缺陷则喜何志丹2 小时前
【巴什博弈 线性筛】P8901 [USACO22DEC] Circular Barn S|普及+
c++·数学·洛谷·巴什博弈·线型筛
样例过了就是过了2 小时前
LeetCode热题100 电话号码的字母组合
数据结构·c++·算法·leetcode·dfs
Yusei_05232 小时前
C++17入门
c++
biubiuibiu2 小时前
选择适合的硬盘:固态与机械硬盘的对比与推荐
c++·算法
LXS_3572 小时前
案例 —— 机房预约系统
开发语言·c++·学习方法