swoole协程是全自动调度的运行时能力,yield是手动驱动的语法糖;前者无需yield即可实现异步i/o,后者仅用于惰性生成数据,混用会导致阻塞失效与协程泄漏。

协程不是生成器,Swoole 的协程和 yield 生成器在底层机制、使用方式、调度权归属上完全不同——混用或误认为“Swoole 协程靠 yield 实现”是常见误解,会导致调试困难、阻塞不生效、甚至协程泄漏。
协程调度完全自动,yield 必须手动控制
Swoole 4.0+ 的协程是内核级全自动调度:只要在 Coroutine\run() 或协程上下文中调用 Co::sleep()、$mysql->query() 等协程化 I/O,底层会自动挂起当前协程、让出 CPU,并在 I/O 完成后精准恢复。你不需要写任何 yield。
而 PHP 原生 yield 是生成器协议,每次暂停/恢复都依赖外部迭代器驱动:$gen->next()、$gen->send() 才能推进。它没有调度器,也不感知 I/O,更不会自动切换上下文。
- 错误写法:
function handler() { yield Co::sleep(1); }—— 这里yield只是把Co::sleep()的返回值(int)当成生成值吐出去,根本没触发协程挂起 - 正确写法:
Coroutine\run(function () { Co::sleep(1); echo "done"; });—— 全自动挂起 + 恢复 - 混合风险:在协程函数里嵌套
yield生成器,若未用Generator::getReturn()或foreach驱动,生成器状态会卡死,协程无法退出
Co::sleep() 和 yield 的语义完全不同
Co::sleep(1) 是一个协程友好的异步等待,它注册事件、让出执行权、由 Swoole 调度器回调恢复;而 yield 是同步的控制流中断,仅保存函数栈帧,不涉及任何事件监听或 I/O 状态管理。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
yield后续必须由外部调用next()或send()才能继续,否则永远停在那里 -
Co::sleep()调用后,当前协程立刻让渡 CPU,1 秒后由事件循环自动唤醒,无需人工干预 - 性能差异明显:1000 个
Co::sleep(1)并发只占少量内存;1000 个未驱动的yield生成器会堆积 1000 个挂起的 Generator 对象,且无超时机制
协程客户端能复用同步代码,yield 生成器不能直接协程化
Swoole 4.1.0+ 提供了 php_stream 兼容层,原生 mysqli、PDO、Redis 等扩展在协程环境里开箱即用——你不用改一行业务逻辑,只要确保它们运行在协程上下文中即可。
但 yield 生成器本身不具备 I/O 感知能力。即使你在生成器里调用了 Co::sleep(),也只是把它当普通函数返回值 yield 出去,不会触发挂起。想靠 yield 实现协程,必须自己实现调度器(比如用 Generator + EventLoop 手动轮询),这早已被 Swoole 的全自动方案淘汰。
- 可复用:
$pdo = new PDO('mysql:host=127.0.0.1', ...); $stmt = $pdo->query('SELECT NOW()');在Coroutine\run()里直接跑 - 不可复用:
function gen() { yield mysqli_query(...); }—— 这行mysqli_query仍是阻塞调用,yield对它毫无影响 - 兼容性陷阱:某些老框架用
yield模拟协程,一旦混入 Swoole 协程环境,容易因上下文错乱导致 MySQL 连接被其他协程复用而报错MySQL server has gone away
真正关键的分水岭在于:Swoole 协程是运行时环境提供的能力,yield 是语言语法糖。前者解决的是“怎么让同步代码不阻塞”,后者解决的是“怎么按需生成数据”。把两者当作同一类东西来理解,会在连接池管理、异常传播、协程生命周期等环节踩到深坑。










