Workerman协程卡死主因是未启用协程环境或混用阻塞操作:需检查eventLoop非null且设为Swoole/Fiber驱动;禁用sleep/file_get_contents等阻塞函数,改用Timer::sleep或http-client异步调用;await必须在async函数内且对象可等待;避免全局变量共享,改用Context::set。

Workerman协程新手常因错误使用同步阻塞操作或不当的协程等待逻辑,导致整个事件循环卡死,所有连接无法响应,服务看似运行但实际已停止处理请求。
确认协程环境已正确启用
在启动脚本顶部执行 var_dump(Workerman\Worker::$eventLoop);,若输出为 null 或 Select 类,说明协程未激活——【此时任何 await 或 Coroutine::create 都不会真正并发,反而可能因隐式同步行为引发假死】。
必须显式指定事件循环驱动:在 Worker 实例创建后、run() 前设置 $worker->eventLoop = Workerman\Events\Swoole::class;(需已安装 Swoole 扩展)或 Workerman\Events\Fiber::class;(PHP ≥ 8.1 且已装 revolt/event-loop)。
禁止在协程中调用阻塞函数
方法一:直接替换系统级阻塞调用
sleep(3) → 改用 Workerman\Timer::sleep(3);file_get_contents('http://...') → 改用 workerman/http-client 的 await $client->get(...)。
方法二:对无法改造的第三方库做协程封装
若必须调用阻塞 SDK(如某支付验签库),用 Coroutine::create() 包裹并配合 Channel 传递结果,避免主线程等待;【切勿在协程内用 stream_socket_client 同步连接,它会冻结整个进程】。
避免 await 与同步代码混用导致的隐式锁
第一步:检查所有 await 表达式是否都位于 async 函数或协程回调内
若在普通函数中写 await $client->get(...),PHP 会报致命错误;若在 onMessage 回调里直接写而未声明 async,则该行被忽略,后续代码继续执行,极易造成逻辑断裂。
第二步:确认 await 对象确实可等待
await 只接受 Generator 或实现了 __await 方法的对象。常见陷阱是误将数组、字符串甚至 null 传给 await,PHP 不报错但协程永远不恢复——此时连接挂起,超时后被强制断开,但其他连接也会因事件循环停滞而排队等待。
第三步:禁用全局变量跨协程共享状态
例如在 onMessage 中修改静态属性 self::$counter++,多个协程并发执行时会覆盖彼此值;改用 Context::set('counter', $val) 绑定到当前协程上下文。

















