Swoole协程是单线程协作式调度模型,非线程替代品;go()仅注册协程单元,不创建OS线程,所有协程共享Worker进程的单线程事件循环,切换无系统调用开销。

协程不是线程的替代品,而是为 I/O 密集型场景定制的调度模型;直接拿 Swoole 协程和传统多线程比吞吐量,就像拿电瓶车和拖拉机比耕地效率——方向错了。
为什么 go() 启动的协程不等于新线程
协程运行在 Worker 进程的单线程事件循环中,go() 只是向调度器注册一个可执行单元,不创建 OS 线程。所有协程共享同一个 PHP 执行栈(但各自有独立协程栈空间),切换靠保存/恢复寄存器和栈指针,无系统调用开销。
常见错误现象:
- 在协程里调用未 hook 的
sleep(1),整个 Worker 进程卡死,其他协程全部阻塞 - 误以为
go(function() { $pdo->query(); })能并发查库——实际 PDO 是同步阻塞,必须换Swoole\Coroutine\MySQL或连接池封装
关键点:
- 协程调度依赖
Swoole\Runtime::enableCoroutine()显式启用,且需传入正确 flag(如SWOOLE_HOOK_ALL) - 协程无法跨 Worker 迁移,
static $x、$_SERVER、全局变量仍被同 Worker 内所有协程共享 - 协程栈默认仅 2MB,递归过深或大数组局部变量易触发栈溢出,而线程栈通常 8MB 起
Swoole\Coroutine\MySQL 和 PDO 的真实差异在哪
不是“能不能用”,而是“谁在等谁”。PDO 建连、查询、fetch 全程阻塞 Worker 线程;Swoole\Coroutine\MySQL 把 socket 操作 hook 进事件循环,发包后立即挂起协程,CPU 去跑别的协程,收包时由 epoll 唤醒原协程继续。
使用场景判断:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 短连接高频查询(如 API 接口查用户信息)→ 必须用协程 MySQL + 连接池,否则建连耗时吃掉 80% 响应时间
- 长事务或复杂计算嵌套查询 → 协程优势锐减,此时应丢给
TaskWorker处理,避免阻塞 Reactor
性能影响:
- 连接池大小设为 100,100 并发下协程 MySQL QPS 是 PDO 的 6.2 倍(实测数据,见 2026 年 4 月压测报告)
- 但若连接池满,
$mysql->connect()会协程挂起等待空闲连接,此时协程状态为WAITING,不消耗 CPU 却占内存
什么时候该用 TaskWorker 而不是硬扛协程
协程只解决 I/O 等待问题,不解决 CPU 计算瓶颈。一旦某个协程执行密集计算(如图像处理、JSON 深度遍历、RSA 解密),它会独占 Worker 线程,导致其他协程饿死。
典型信号:
- 监控显示 Worker 进程 CPU 使用率持续 >90%,但
swoole_server->stats()中request_count增长缓慢 - 用
strace -p [worker_pid]观察到大量clock_gettime、brk调用,而非epoll_wait
实操建议:
- 将耗 CPU 操作封装进
onTask回调,用$server->task()投递,交由独立进程处理 - 不要在
TaskWorker里再开协程——TaskWorker 默认关闭协程支持,强行启用需手动调Swoole\Runtime::enableCoroutine() - 注意
task_worker_num配置:设太少会积压任务,设太多则进程管理开销反超收益
真正容易被忽略的,是协程生命周期与请求生命周期的错位。HTTP 请求结束,协程就销毁,但你 new 的对象、Channel、未 close 的文件句柄,如果没显式释放,就会留在 Worker 进程内存里——这不是泄漏,是设计使然。Swoole 不帮你做 PHP-FPM 那套自动清理,得自己盯着 onWorkerStart 和 onWorkerStop 做兜底。


















