CHAPTER 04
Coroutine 协程
对应文件:
- 接口:
include/Coroutine.h - 实现:
src/Coroutine.cpp
1. 模块定位
该模块实现了跨平台协程与调度器:
- Linux:
ucontext - Windows:Fiber
目标是提供用户态协作式并发,并包含工作窃取与时间片抢占标记能力。
2. 核心对象
2.1 Coroutine
- 状态:
READY / RUNNING / SUSPEND / DONE - 核心接口:
resume()suspend()state()
- 上下文资源:
- Linux:
ucontext_t + stack - Windows:
LPVOID fiber_
- Linux:
2.2 Scheduler
- 单例:
Scheduler::instance() - 队列模型:
- 全局队列
globalQueue_ - 每个 worker 的本地队列
localQueue
- 全局队列
- 并发组件:
- worker 线程集合
workers_ - 抢占线程
preemptThread_ - 条件变量
cv_
- worker 线程集合
3. 线程局部上下文(TLS)
tls_current_corotls_current_schedulertls_current_worker- 平台补充:
- Linux:
tls_scheduler_ctx - Windows:
tls_scheduler_fiber
- Linux:
它们用于在 resume/suspend/yield 期间定位当前执行上下文。
4. 关键执行流程
4.1 协程构造与入口
- 构造函数分平台创建上下文
- entry/fiberEntry 执行
func_ - 执行结束后设置
DONE并suspend()回到调度器
4.2 resume()
- 仅对未
DONE协程生效 - 更新状态
RUNNING - 设置
tls_current_coro = this - 平台切换:
- Linux:
swapcontext(&tls_scheduler_ctx, &ctx_) - Windows:
SwitchToFiber(fiber_)
- Linux:
4.3 suspend()
- 仅在存在
tls_current_scheduler时有效 RUNNING -> SUSPEND- 切回调度器上下文
4.4 Scheduler::run_on_worker_thread()
循环调度策略:
- 取本地任务
- 取全局任务
- 从其他 worker 窃取任务
- 无任务则等待
cv_
任务执行后:
- 若协程未完成,重新放回本地队列
- 若完成则释放共享指针生命周期
5. 抢占相关能力
preemptLoop()每个时间片将 worker 的preemptRequested=trueshouldPreempt()读取并清除该标记safe_point()在安全点触发yield()sleep_for()休眠后调用safe_point()
注意:这是“协作式抢占标记”,不是内核级硬中断抢占。
6. 任务提交行为
submitTask()若调度器未启动,会start(1)- 若调用线程本身是该调度器 worker,则优先入本地队列
- 否则入全局队列并
notify_one
7. 已知特点与风险
- 优点:
- 跨平台上下文切换封装完整
- 本地队列 + 全局队列 + work stealing 结构清晰
- 有时间片抢占信号机制
- 风险:
- 业务代码需要主动
safe_point/yield,否则会长时间占用 worker - 与网络 I/O 主链路尚未完全融合,当前 HTTP 主流程仍主要是线程池同步处理
- 业务代码需要主动
8. 面试回答模板(简版)
“我们协程是用户态实现,Linux 用 ucontext,Windows 用 Fiber。调度器采用全局队列 + 本地队列 + work stealing。抢占不是强制中断,而是通过时间片标记和 safe_point 协作让出。当前协程基础设施已成熟,下一步是把 I/O 事件驱动与协程调度彻底打通。”