yuqi-zheng

Ray 异步基础设施(三):线程池 `IOServicePool` 与定时器 `PeriodicalRunner`


Ray 异步基础设施系列第三篇。← 上一篇:可观测性与混沌测试 · 下一篇:gRPC 与 Asio 的协作模式 →


1. IOServicePool:事件循环线程池

架构

┌─────────────────────────────────────────────┐
│               IOServicePool                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │io_ctx[0] │  │io_ctx[1] │  │io_ctx[N] │   │
│  │ Thread 0 │  │ Thread 1 │  │ Thread N │   │
│  └──────────┘  └──────────┘  └──────────┘   │
└─────────────────────────────────────────────┘

每个 io_context 拥有独立的线程,线程间完全隔离,业务逻辑在各自线程内串行执行,无需加锁。

初始化与运行

IOServicePool pool(4);  // 创建 4 个 io_context,各自独立线程
pool.Run();             // 启动所有线程(内部使用 work_guard 保持运行)

获取 io_context 的两种策略

方法策略适用场景
Get()轮询(Round-Robin)无状态短任务,均匀分散负载
Get(hash)哈希固定需要线程亲和性的对象(如同一连接的所有回调必须在同一线程上串行执行)

哈希固定策略是关键:同一连接的所有请求映射到同一个 io_context,保证它们按序串行执行,无需额外同步。


2. PeriodicalRunner:周期性任务调度器

使用方式

auto runner = PeriodicalRunner::Create(io_context);
runner->RunFnPeriodically([] { SendHeartbeat(); }, 1000, "Heartbeat");

在指定的 io_context 上,每隔 1000ms 执行一次 SendHeartbeat

生命周期安全设计

PeriodicalRunner 继承 std::enable_shared_from_this<PeriodicalRunner>,在定时器回调中捕获 weak_from_this()

timer.async_wait([weak_self = weak_from_this(), fn, period, timer](const auto& error) {
    if (auto self = weak_self.lock()) {
        if (error == boost::asio::error::operation_aborted) return;
        fn();
        self->DoRunFnPeriodically(fn, period, timer);
    }
    // weak_self.lock() 失败 → 对象已析构 → 静默退出
});

当外部释放 shared_ptr<PeriodicalRunner> 后,下一次定时器触发时 lock() 返回 nullptr,周期循环自动终止,无崩溃、无泄漏、无需显式取消。

这正是《C++ 异步编程:用 weak_from_this 安全捕获对象生命周期》中介绍的模式在生产代码中的应用。

仪表化执行

当全局配置 event_stats 开启时,PeriodicalRunner 会额外记录从定时器到期回调实际执行之间的排队延迟,增强端到端可观测性。


3. 两者的协作关系

IOServicePool::Get(hash) → instrumented_io_context*

                        PeriodicalRunner::Create(*io_context)

                        runner->RunFnPeriodically(...)
  • IOServicePool 提供多线程运行载体(解决并发与负载均衡)
  • PeriodicalRunner绑定在某一载体上的闹钟(解决周期调度与生命周期安全)

Ray 中几乎所有后台定时逻辑(心跳发送、监控上报、GC 触发)都由这两个组件驱动。


总结

  • IOServicePool 提供无锁并发:多线程各自运行独立 io_context,线程亲和性由哈希保证。
  • PeriodicalRunner 通过 weak_from_this() 实现了生命周期安全的周期调度:所有者析构后定时回调自动退出。
  • 两者组合,构成了 Ray 后台任务调度的基础设施。

下一篇:gRPC 与 Asio 的协作模式 →