直接调用Coroutine::count()可获取当前活跃协程总数,返回已启动未结束的全部协程数量;需结合swoole_coroutine_stats()查看各协程状态、堆栈及存活时间,CID不可作为长期标识,应以state和elapsed_time为判断依据。

如何获取当前活跃协程总数
直接调用 Coroutine::count() 就能拿到当前正在运行(含等待 I/O 的)协程数量。它不区分“用户创建”还是“框架内部协程”,返回的是内核调度器维护的全部活跃协程数。
这个值在调试时非常关键——比如你发现服务响应变慢,先看它是否持续高于 2000;如果每秒新增几百个却不见下降,大概率有协程没退出或被阻塞住了。
- 注意:该函数返回整数,不是数组,不能遍历单个协程
- 它统计的是“已启动未结束”的协程,哪怕协程里只执行了一行
sleep(1)也算在内 - 在
onWorkerStart或定时器中调用最稳妥,避免在请求回调里因并发干扰读数
怎么查看每个协程的堆栈和状态
仅知道总数不够,得定位具体是哪个协程卡住。Swoole 提供了 swoole_coroutine_stats(),它返回一个包含详细信息的数组,其中 coroutines 键下是每个活跃协程的快照。
典型用法是在定时器里打印:
use Swoole\Coroutine;
Swoole\Timer::tick(5000, function () {
$stats = swoole_coroutine_stats();
foreach ($stats['coroutines'] as $cid => $info) {
if ($info['state'] === SWOOLE_COROUTINE_RUNNING) {
echo "[CID {$cid}] running, stack: " . $info['stack'] . "\n";
}
}
});
关键字段说明:
-
cid:协程 ID,唯一标识 -
state:状态码,SWOOLE_COROUTINE_WAITING表示在等 I/O,SWOOLE_COROUTINE_RUNNING表示正在执行 PHP 代码 -
stack:协程当前调用栈(字符串),能看出卡在哪一行、哪个函数 -
elapsed_time:该协程已存活毫秒数,超过 5000ms 还没结束就要警惕
为什么 Coroutine::list() 不存在
Swoole 没有提供类似 Coroutine::list() 这样的 API,不是遗漏,而是设计取舍。协程是轻量级用户态调度单元,其生命周期极短(毫秒级),且大量用于底层 I/O 调度(如 DNS 查询、文件读写),暴露全部协程列表会带来可观测性开销和内存压力。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
所以官方只提供聚合统计(Coroutine::count())和采样快照(swoole_coroutine_stats()),而非实时枚举。强行轮询所有协程 ID 并逐个查状态,在高并发下反而可能拖慢服务。
如果你真需要“列出所有”,只能靠 swoole_coroutine_stats() 的 coroutines 数组——但它只返回当前时刻活跃的协程,且最多返回前 1000 个(受内核限制),超出部分会被截断。
容易被忽略的陷阱:协程 ID 重用与误判
协程 ID 是递增整数,但会循环复用。一个 CID 为 123 的协程退出后,新创建的协程可能再次拿到 123。因此不能把 CID 当作长期身份标识,也不能基于 CID 做缓存或映射。
更危险的是:如果你在协程里用了 defer 或闭包持有外部变量,而这个协程又没正常结束(比如被 exit 中断或发生致命错误),它的状态可能滞留在 swoole_coroutine_stats() 中长达几秒,导致你以为还有“活协程”,实际只是残留元数据。
真正可靠的判断依据只有两个:state 字段是否为 SWOOLE_COROUTINE_RUNNING 或 SWOOLE_COROUTINE_WAITING,以及 elapsed_time 是否异常偏大。其他字段(如 cid、stack)仅供临时诊断,不可持久化依赖。

















