workerman协程必须主动响应取消信号以避免资源泄漏,需通过co::getcanceller获取取消器并轮询iscancelled(),封装可取消sleep(如co::select或channel+定时器),并用co::defer注册清理逻辑且按顺序执行四步。

在Workerman中使用协程时,若未主动响应取消信号,协程可能被强制终止导致资源泄漏、数据库连接未释放、文件句柄残留或回调丢失。必须让协程能感知并主动退出,而非依赖底层强行 kill。
监听协程取消信号
协程运行期间需持续检查是否已被取消,否则 cancel() 调用无效。
调用 co::getuid() 获取当前协程 ID,再用 Co::getCanceller($cid) 获取对应取消器对象。
调用 $canceller->isCancelled() 判断状态,一旦返回 true,立即执行清理并 return。
这一步不能省略:Workerman 的协程取消是协作式而非抢占式,【不主动轮询 isCancelled() 就永远不会知道已被取消】。
封装可取消的 sleep
原生 co::sleep() 不响应取消,会导致协程卡死在休眠中无法退出。
方法一:用 Co::select() 模拟带取消的 sleep
创建一个空管道 $pipe = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);,然后对读端调用 Co::select([$pipe[0]], [], [], $seconds);若 select 返回 false 或超时前被取消,则跳出。
方法二:改用 Co::wait() + 定时器
启动一个一次性定时器,到期后向 channel 发送信号;同时用 channel->pop($timeout) 等待,若 channel 为空且超时,则视为 sleep 完成;若 channel 在此之前收到 cancel 信号,直接退出。
注意:不要在 sleep 中直接写死 co::sleep(5),【它完全忽略 cancel,协程将彻底失联】。
统一注册取消清理逻辑
第一步:定义清理函数,如关闭数据库连接、释放锁、关闭文件句柄。
第二步:调用 Co::defer($cleanup) 注册延迟执行,确保无论协程如何退出都会触发。
第三步:在主循环中插入 if ($canceller->isCancelled()) { $cleanup(); return; } 检查点。
第四步:外部调用 Co::cancel($cid) 后,该协程下次进入检查点即退出。
这四步顺序不可颠倒——defer 必须在协程刚启动时注册,否则后续分配的资源可能来不及注册就已取消。











