协程调度器必须显式启动(如co\run())才生效,否则默认同步阻塞;仅在hook后的i/o点触发切换;协程栈独立但全局变量共享,易致内存泄漏;taskworker非协程安全,需手动启用且无法复用worker资源。

协程调度器不是“自动运行”的魔法模块,它只在明确启用协程环境后才接管控制流;没调用 Co\run() 或 go(),就根本没有调度器什么事。
协程调度器必须显式启动,否则不工作
很多人以为只要用了 Swoole 就默认走协程调度,其实完全相反:Worker 进程启动后默认是同步阻塞模型,所有函数(包括 sleep()、file_get_contents())都会直接卡死整个进程。
-
Co\run()是最安全的入口:它创建一个顶层协程容器,所有子协程都受其调度器管理 -
go(function () { ... })只在已有协程上下文中才有效;在onRequest回调里直接写go(),若外层没Co\run()包裹,会报Coroutine is not running - Hyperf 等框架已内置
Co\run(),但手写 Swoole Server 时极易漏掉——这是线上 CPU 100% 的常见原因
协程切换只发生在 Hook 后的 I/O 阻塞点
调度器不会主动切协程,它只在检测到「当前协程进入可挂起状态」时才介入。这个状态不是由代码行号决定的,而是由底层是否触发了被 Hook 的系统调用决定的。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 未启用
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)时,curl_exec()仍是同步阻塞,调度器无法感知,也就不会切 - 启用了
SWOOLE_HOOK_FILE后,fopen()才变成非阻塞,此时读文件才会触发挂起+唤醒流程 - 哪怕写了
co::sleep(1),如果没在协程上下文中执行,它退化为usleep(),照样阻塞整个 Worker
协程栈独立,但全局变量共享——内存泄漏主因
每个协程有自己的一份栈空间(默认 2MB),但 $_SERVER、static $x、global $y 这些变量全进程共享。一次请求往 static 里塞了 10MB 数组,下次请求还在那儿。
- 协程结束 ≠ 变量销毁:PHP-FPM 里脚本退出就清空,Swoole 常驻进程里这些变量一直活着
- 别依赖
onClose清理:WebSocket 连接断开才触发,HTTP 请求压根没这回调 - 真正可控的方式只有两种:
Swoole\Table(进程内共享且可设 TTL)或外部存储(Redis)
协程不能跨线程迁移,TaskWorker 不是协程安全区
很多人误以为把耗时操作扔进 TaskWorker 就能“躲开协程限制”,但 TaskWorker 默认是同步阻塞模型,且和 Worker 进程内存完全隔离——你在 Worker 里 go() 创建的协程,根本进不了 TaskWorker。
- TaskWorker 中调用
co::sleep()会直接报错,因为没启动协程环境 - 想在 TaskWorker 里用协程?得手动加
Co\run(),且不能复用 Worker 的连接池(如 MySQL 连接句柄无法跨进程传递) - 真正适合 TaskWorker 的,是图像处理、PDF 生成等纯 CPU 密集型任务,而不是“换个地方继续用协程”
协程调度器本身不复杂,难的是理解它和 PHP 运行时的耦合边界:它不接管所有代码,只干预被 Hook 的 I/O;它不清理变量,只管理栈帧;它不跨进程,也不替你做资源隔离。这些边界一旦模糊,问题就会从性能下降迅速滑向内存爆炸或状态污染。










