Coroutine::listCoroutines() 返回当前活跃协程的完整 CID 列表,其数组长度即实时活跃协程数,是唯一无侵入、低开销的实时快照方式;Coroutine::count() 则返回进程启动以来未彻底退出的协程累计总数,用于检测泄漏。

直接用 Coroutine::listCoroutines() 获取当前协程 ID 列表
这不是“数量”而是“全部 ID”,但它是唯一可靠、无侵入的实时协程快照方式。Coroutine::listCoroutines() 返回一个整数数组,每个元素是一个正在运行的协程 CID。数组长度就是当前活跃协程数。
- 它不统计已结束或被销毁的协程,只反映此刻调度器里还活着的协程
- 调用开销极低(微秒级),比
getBackTrace()安全得多,可高频用于监控(如每秒一次) - 注意:协程刚创建但尚未执行到第一个挂起点(比如刚进
go()函数体第一行),也会出现在列表中 - 别在非协程环境(如 PHP-FPM 或 Swoole 主进程未启动协程调度时)调用,会返回空数组或报错
Coroutine::count() 返回的是“已创建但未结束”的协程总数
Coroutine::count() 是更轻量的计数接口,但它统计的是从进程启动以来所有创建过、且尚未彻底退出的协程累计数——不是当前活跃数,也不清零。
- 它适合做趋势观察(比如持续上涨说明有协程泄漏),但不能替代
listCoroutines()做实时水位判断 - worker 进程重启后该值重置,但单次运行期间不会自动减去已退出协程
- 如果你看到
Coroutine::count()持续增长,而Coroutine::listCoroutines()长度稳定,大概率是协程里用了sleep()或阻塞 I/O 导致协程卡住未退出
为什么不能只看 $server->stats()['connection_num']?
连接数和协程数完全不是一回事。一个 HTTP 连接可能对应 1 个协程(短连接),也可能复用为多个协程(长连接+HTTP/2),甚至一个协程里嵌套发起多个协程请求(比如查 DB + 调 API)。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
connection_num只告诉你 TCP 层有多少 socket 还开着,对协程调度没意义 - 高并发下
connection_num很低但listCoroutines()上千,说明大量协程在等 DB、Redis 或远程 API —— 这才是真实压力点 - 反过来,
connection_num很高但协程数很低,可能是连接没及时 close,或者用了连接池但没正确释放
生产环境监控建议:组合使用 + 设置阈值告警
单纯看数字没用,得结合业务节奏和资源限制设边界。
- 记录
Coroutine::listCoroutines()长度每 5 秒一次,画成折线图;突增或长期高于 500(视 worker 内存而定)就要排查 - 把
Coroutine::count() - count(Coroutine::listCoroutines())当作“疑似泄漏差值”,大于 100 就触发日志采样 - 避免在
onRequest里直接调listCoroutines()打点,改用swoole_timer_tick(5000, ...)异步采集 - 别依赖单次
getBackTrace()定位问题——它本身会暂停协程,只在报警后手动触发一次即可
协程数量监控的关键不在“怎么取”,而在“什么时候取”和“取完怎么解读”。listCoroutines() 返回的是活的协程快照,count() 是粗粒度累计计数,两者互补,但都不能代替对协程生命周期的理解。真正难的不是采集,是判断哪个协程该结束却没结束。

















