协程切换发生在I/O阻塞点或显式让出时,如Co::sleep()、channel操作或被Hook的I/O调用;此时协程置为SUSPENDED并由用户态调度器通过保存/恢复寄存器与栈指针完成微秒级切换。

协程上下文切换不是操作系统调度,而是用户态寄存器+栈指针的保存与恢复,开销在微秒级,但必须靠 Runtime Hook 才能生效。
协程切换到底发生在哪一刻
协程不会主动让出 CPU,只在明确的阻塞点触发切换:比如 Co::sleep()、channel->pop()、或任何被 Hook 的 I/O 操作(如 Co\MySQL->query()、Co\Http\Client->get())。一旦底层调用进入 swoole_coroutine_read() 这类封装函数,且当前 fd 尚未就绪,协程立即置为 SUSPENDED 状态,并跳转到事件循环调度下一个可运行协程。
常见错误现象:
- 写了
go(function () { sleep(1); });,结果整个 Worker 进程卡住 1 秒 ——sleep()是原生阻塞函数,未被 Hook,必须改用Co::sleep(1) - 用
file_get_contents()请求远程 API,协程不挂起、并发数上不去 —— 该函数默认不走协程 Hook,需先调用Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_FILE)
为什么启用 SWOOLE_HOOK_ALL 不等于万事大吉
Hook 是分层生效的:SWOOLE_HOOK_ALL 仅覆盖 Swoole 自己封装的 socket 系列函数(如 connect、read、write),对第三方扩展中直接调用 libc 或 librdkafka 的底层 I/O 无效。例如 Kafka 客户端若基于 rdkafka 扩展,其 rd_kafka_poll() 不会被拦截,协程仍会阻塞。
使用场景判断要点:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 确认你用的客户端是否为 Swoole 原生协程版(如
Swoole\Coroutine\Kafka);否则得降级到 TaskWorker 处理 - 开启 Hook 后,务必检查
strace -p $pid -e trace=epoll_wait,read,write,验证是否真进到了非阻塞路径 -
Swoole\Runtime::enableCoroutine()必须在所有协程启动前调用,放在onWorkerStart里就晚了
协程栈独立但全局变量共享带来的陷阱
每个协程有自己 2KB~8KB 的栈空间,但所有协程共享同一份进程内存,包括静态变量、全局数组、单例对象。这意味着 static $req_id = 0; $req_id++ 在并发请求中会互相污染。
容易踩的坑:
- 在
onRequest回调里用$_SERVER或$_GET—— 这些超全局变量在 Swoole 中无效,应从$request->get显式取值 - 误以为
Co::getContext()能自动绑定请求上下文 —— 它只是返回当前协程 ID,真正做上下文隔离得靠手动传参或Co::pack()/Co::unpack() - 协程内 new 出的对象未及时 unset,又引用了闭包或 PDO 实例,导致栈无法回收、内存缓慢泄漏
协程无法跨 Worker 迁移的硬约束
Swoole 的“多进程 + 单线程协程”模型决定了:一个协程从 start 到 finish 必须在同一个 Worker 进程内完成。它不能像 Go routine 那样被调度器迁移到其他 OS 线程,也不能在不同 Worker 间传递句柄(如 MySQL 连接、Redis client)。
性能与兼容性影响:
- 连接池必须按 Worker 进程维度初始化,不能共用一个全局连接对象
- 想实现“请求级上下文热迁移”(如 LLM 流式响应中断后续传),必须依赖外部存储(Redis)+ 请求 ID 对齐,而非协程本身
- TaskWorker 执行完后,回调函数仍在原 Worker 协程中执行,所以回调里仍可访问该协程的局部变量,但不能再操作已关闭的 socket 句柄
真正难的不是理解“挂起/恢复”,而是判断哪个函数调用会实际触发切换、哪些看似异步的操作其实偷偷阻塞了整个 Worker —— 这需要结合 strace、swoole_server->stats() 和日志里的协程 ID 才能准确定位。

















