协程切换由I/O驱动,co::sleep(0)不保证让出,应改用co::yield();defer在异常终止时不执行,推荐Swoole\Coroutine::defer()或try/finally;闭包需use($this)访问对象;全量Hook可能导致MySQLi静默失败,应按需开启。

协程切换不是手动控制的,别在 go 里写 co::sleep 来“等切换”
很多人以为加个 co::sleep(0) 就能主动让出协程、触发调度,其实这是误解。Swoole 的协程调度是 I/O 驱动的,co::sleep(0) 并不保证立即切走,尤其在无其他协程待调度时,它可能直接返回,甚至被优化掉。
真正需要让出控制权的场景(比如避免长循环饿死其他协程),应该用 co::yield();但要注意:它只对当前协程生效,且必须在协程上下文中调用——在非协程环境(如 onReceive 回调但没用 go 包裹)里调用会报 Coroutine is not running 错误。
-
co::sleep(0)不等于 yield,慎用于“强制切换”目的 - 循环中每 N 次迭代插入一次
co::yield(),比固定间隔 sleep 更可靠 - 如果已在
go内,且下一行是阻塞 I/O(如co::read),通常不需要手动 yield —— 调度器会在等待时自动切走
defer 在协程退出时不一定执行,别依赖它做资源清理
defer 看似像 Go,但在 Swoole 协程里行为受限:它只在协程正常结束(函数 return)时触发;协程被强制终止(如超时 kill、父协程 exit)、或发生致命错误(如未捕获的 Fatal error)时,defer 回调不会运行。
常见踩坑是用 defer 关数据库连接、关文件句柄,结果协程异常退出后连接泄漏。真实生产环境更推荐显式管理:用 try/finally,或把资源绑定到协程 ID 上,配合 Swoole\Coroutine::defer()(注意这是静态方法,和 PHP 的 defer 关键字不同)。
- PHP 8.0+ 的
defer关键字仅作用于当前函数作用域,且不跨协程生命周期 -
Swoole\Coroutine::defer()注册的回调,会在协程结束时执行,包括异常退出 —— 但前提是协程尚未被底层强制回收(如超时时间设得太短) - 连接池类资源(如
RedisPool)应优先走->put()归还,而不是等 defer 关闭
协程上下文丢失:为什么 $this 在 go 里变 null 或报错?
在类方法中直接写 go(function () { var_dump($this); });,运行时大概率报 Using $this when not in object context。这不是 bug,而是闭包默认不继承外部作用域的 $this —— 即使你在协程里,PHP 也不会自动绑定。
解决办法只有显式 use:go(function () use ($this) { ... });。但要注意:这传递的是对象引用,不是协程隔离副本。如果多个协程并发修改同一对象属性,仍需自行加锁或避免共享状态。
- 匿名函数内访问
$this必须显式use ($this),否则语法错误 - 若类方法本身是 static,
use (static::class)无法替代$this,得传实例或改用依赖注入 - 更安全的做法是把需要的数据提前 extract 成变量,再
use ($id, $data),减少隐式依赖
Co::set(['hook_flags' => SWOOLE_HOOK_ALL]) 开太猛,MySQLi 可能静默失败
全量 Hook 看似省事,但 SWOOLE_HOOK_ALL 会劫持所有系统调用,包括部分扩展的内部 socket 操作。MySQLi 在某些版本(尤其是 PHP 7.4 + mysqlnd 8.0.30 前)遇到 SWOOLE_HOOK_STREAM | SWOOLE_HOOK_SOCKET 组合时,查询可能卡住、返回空结果,且不抛异常,日志里也看不到错误。
推荐按需开启:Web 场景一般只需 SWOOLE_HOOK_TCP(覆盖 curl、PDO MySQL、Redis 等主流客户端);如果用了 mysqli,务必单独测试,或降级为 PDO + mysql:host=127.0.0.1(走 TCP 而非 socket 文件)。
-
SWOOLE_HOOK_ALL在 Swoole v5.0+ 已标记为 legacy,新项目应避免 - 用
Co::getHookFlags()检查当前生效的 flag,确认是否真被覆盖 - 本地开发开全量没问题,但上线前必须压测 MySQLi 查询链路,观察超时和空响应率
协程不是魔法,它的“隐藏”往往藏在调度边界、资源生命周期和 Hook 粒度里。最常出问题的,从来不是你没写的代码,而是你以为“自动发生”的那部分。










