协程调度器到底在调度什么?从 inline_scheduler 到工作窃取线程池
协程调度器到底在调度什么?从 inline_scheduler 到工作窃取线程池 本专栏文章:拆开 cppcoro 给你看 · 第 5 篇 前几篇我们一直在协程"内部"兜圈子——task 怎么设计、同步原语怎么写、并发怎么编排。但有一个问题一直没认真回答:协程在哪个线程上执行? co_await 之后你可能在任何线程上恢复——这取决于谁调了 handle.resume()。大部分时候这不是问题,但有时你确实想控制恢复的线程。这就是调度器的概念。 本文分三层递进: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Layer 1: 调度器概念(30 分钟能读完) ├─ inline_scheduler — 22 行,不调度也是调度 ├─ round_robin_scheduler — 125 行,对称转移调度的杰作 └─ schedule_on vs resume_on Layer 2: 工作窃取线程池(核心) ├─ 本地 LIFO 队列 ├─ 全局 MPSC 队列 ├─ 偷取 (work-stealing) └─ 睡眠/唤醒协议 Layer 3: IOCP + 取消 + 文件/网络 I/O ├─ OVERLAPPED 嵌入 awaiter (CRTP) ├─ 可取消操作的四态状态机 ├─ cancellation_token/source/registration ├─ io_service + io_work_scope └─ async_scope Layer 1:调度器概念 问题:协程在哪个线程上执行? 1 2 3 4 5 6 task<> my_coro() { std::cout << "I'm on thread " << std::this_thread::get_id() << std::endl; co_await some_io(); std::cout << "Now I'm on thread " << std::this_thread::get_id() << std::endl; // ↑ 恢复后可能在完全不同的线程上! } 1.1 inline_scheduler —— “不调度"也是调度 1 2 3 4 5 6 class inline_scheduler { public: std::suspend_never schedule() const noexcept { return {}; // 不挂起,原地继续 } }; 返回 suspend_never 意味着 co_await scheduler.schedule() 等价于什么都不做。但它在泛型代码中有实际意义——你的函数接受一个调度器参数,inline_scheduler 就是"不需要调度的调度器”。 ...