co::run是Swoole协程调度器唯一入口,仅限CLI模式且只能调用一次;它启动事件循环并挂起主线程,要求所有I/O操作使用协程API(如Co::sleep),禁止嵌套或混用同步阻塞函数。

co::run 是 Swoole 协程调度器的入口,必须在 CLI 环境中调用,且只能执行一次
它不是普通函数,而是协程运行时的启动开关。一旦调用 co::run(),Swoole 就接管当前进程,进入协程事件循环;此时 PHP 主线程被挂起,所有后续同步代码(比如 echo、sleep())将不再执行,除非它们被包裹在协程内或通过协程 API 调用。
- 不能在 Web SAPI(如 Apache/FPM)中使用,会直接报错
Fatal error: Uncaught Swoole\Error: co::run can only be used in CLI mode - 不能重复调用,第二次调用会触发
Fatal error: co::run has already been started - 不支持嵌套 —— 不能在已运行的
co::run()内部再调用它 - 退出后无法恢复,整个进程生命周期结束(除非你显式
exit()或自然跑完)
最简可用写法:用匿名函数传入协程逻辑
co::run() 接收一个可调用对象(通常是 Closure),该闭包会在协程上下文中执行。闭包里写的代码就自动获得协程能力,比如可直接用 co::sleep()、Co\Http\Client 等。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
<?php
use Swoole\Coroutine as Co;
co::run(function () {
echo "协程已启动\n";
Co::sleep(1);
echo "1 秒后执行\n";
});
- 闭包参数为空即可,无需手动传入协程 ID 或上下文
- 闭包内抛出未捕获异常会导致
co::run()立即退出,并打印堆栈(类似普通脚本) - 若需传参,可用
use ($var)捕获外部变量,但注意变量生命周期 —— 外部作用域结束后,use的引用仍有效
常见错误:混用同步阻塞函数导致协程“假死”
很多人以为只要写了 co::run(),所有代码就自动协程化了。其实不然:file_get_contents()、curl_exec()、sleep() 这类原生函数仍是同步阻塞的,会卡住整个协程调度器,让其他协程无法切换。
- ✅ 正确做法:改用协程版 API,例如
Co\Http\Client替代curl_exec(),Co::sleep()替代sleep(),Co\MySQL替代mysqli_connect() - ❌ 错误示例:
co::run(function () { sleep(5); });—— 这 5 秒内整个协程环境停摆,无法并发 - ⚠️ 特殊情况:PHP 8.1+ 启用
zlib.output_compression或某些扩展可能隐式触发同步 I/O,建议关闭或测试确认
配合 defer、deferWithContext 实现资源清理
协程退出前常需释放连接、关闭文件句柄或上报状态。co::defer() 和 co::deferWithContext() 是唯一可靠的清理机制 —— 它们注册的回调会在当前协程结束(无论正常 return 还是异常退出)时执行。
<?php
co::run(function () {
$client = new Co\Http\Client('httpbin.org', 443, true);
co::defer(function () use ($client) {
$client->close();
echo "HTTP 客户端已关闭\n";
});
$client->get('/');
echo "请求完成\n";
});
-
co::defer()绑定到当前协程,协程结束即触发;co::deferWithContext()可绑定到指定协程 ID,适合在子协程中注册清理逻辑 - 多个
defer按注册逆序执行(LIFO),类似finally堆栈 - 不能在
defer回调里再开新协程(如go()),否则可能引发调度混乱或内存泄漏
co::run() 一旦开始,就完全依赖你写的逻辑来决定何时退出。最容易被忽略的是:没意识到「协程不是多线程」,所有协程共享同一 OS 线程,CPU 密集型操作(如大数组排序、JSON 解析)依然会阻塞调度 —— 这类任务得交给 Co\Process 或投递到线程池处理。

















