Workerman协程环境下主进程需等待协程结束再退出,否则导致连接中断、数据丢失或资源泄漏;须配置eventLoop为协程驱动,正确注册onWorkerStop回调并用Channel或Barrier协调协程退出,最后可强制清理残留协程。

Workerman协程环境下,当主进程收到关闭信号时,必须确保所有正在运行的协程任务处理完毕再退出,否则会出现连接中断、数据丢失或资源泄漏。直接 kill 或未等待协程结束就退出,会导致部分异步操作被强行截断。
确认协程运行环境已启用
检查 config/process.php 中对应进程的 eventLoop 配置是否明确指定为协程驱动:
必须设置为 Workerman\Events\Swoole::class、Workerman\Events\Swow::class 或 Workerman\Events\Fiber::class 之一;空字符串或未配置将退化为非协程模式,后续所有协程关闭逻辑均无效。
若使用 Workerman 5.0+,还需确认已通过 require_once __DIR__ . '/vendor/autoload.php'; 加载自动加载器,且未手动调用 Worker::run() 前启动了阻塞操作(如 sleep())。
在 onWorkerStop 中触发协程终止信号
在 Worker 实例上注册 onWorkerStop 回调,这是唯一能保证被调用的进程退出前钩子:
在回调中向所有活跃协程广播关闭信号,推荐使用全局 Channel 或 context.Context 模拟(Workerman 本身不提供原生 Context,需自行封装)。
例如:创建一个 $shutdownChan = new Channel(1),在 onWorkerStop 中执行 $shutdownChan->push(true),所有长期运行的协程需监听该 channel 并主动退出循环。
协程内主动响应关闭信号
方法一:使用 for-select 模式监听 shutdown channel
在协程主体中写入如下结构:
go(function () use ($shutdownChan) { for { select { case pop(): return; // 协程安全退出 default: // 执行实际业务逻辑 } }});
⚠ 注意:不能省略 default 分支,否则 select 会永久阻塞,无法响应 shutdown 信号。
方法二:使用协程屏障(Barrier)配合超时等待
适用于多个协程协同工作场景。先创建 Barrier 实例:$barrier = new Barrier();
每个协程完成自身任务后调用 $barrier->wait();,主进程在 onWorkerStop 中调用 $barrier->wait(); 并设超时(如 5 秒),避免无限等待。
这一步必须在 close($todo) 类操作之后执行,否则 barrier 可能永远等不到全部协程抵达。
强制清理残留协程(兜底方案)
第一步:调用 Coroutine::stats() 获取当前存活协程数量与 ID 列表(仅 Swoole/Workerman+Swoole 环境支持)。
第二步:遍历协程 ID,对每个协程执行 Coroutine::cancel($cid) —— 但此操作不可逆,仅限最后 3 秒倒计时阶段使用,且必须确保该协程无关键 I/O 或事务未提交。
第三步:调用 gc_collect_cycles() 强制触发 PHP 垃圾回收,释放因协程挂起导致的内存引用。

















