go函数必须在协程上下文中调用,否则静默失效;需确保已进入swoole\coroutine\run或server自动创建的协程环境;禁止引用传递、同步阻塞调用及未释放资源。

go函数必须在协程上下文中调用
直接在主线程(非协程环境)里调用 go 会报错或静默失败,常见现象是代码没执行、无日志、也不抛异常。Swoole 的 go 函数依赖当前存在有效的协程调度器,比如已进入 Swoole\Coroutine\run 或由 HttpServer/TcpServer 自动启动的协程环境。
典型错误写法:
// ❌ 错误:未进入协程环境就调用 go
go(function () {
echo "hello";
});
- 启动协程必须显式包裹在
Swoole\Coroutine\run中,或由 Swoole Server(如HttpServer)自动触发 - 命令行脚本需手动启动协程调度器:
Swoole\Coroutine\run(fn),否则go不生效 - Web 请求中(如
onRequest回调),Swoole 已自动创建协程,此时可直接用go
go函数参数不能是引用传递的匿名函数
PHP 中闭包默认按值捕获变量,但若你在 go 里传入带 &$var 引用的匿名函数,协程间共享变量会导致竞态——因为多个协程可能同时读写同一内存地址,而 PHP 协程不是线程,不提供原子性保障。
常见错误场景:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
$data = [];
go(function () use (&$data) { // ❌ 危险:多个协程并发修改 $data
$data[] = 'a';
});
- 避免使用
use (&$var),改用值传递或协程安全的数据结构(如Swoole\Coroutine\Channel) - 若需共享状态,优先走
Channel或WaitGroup,而非全局/引用变量 - 注意:PHP 变量本身是写时复制(Copy-on-Write),但数组/对象引用计数机制在协程切换时并不保证线程安全
子协程内不能直接使用同步阻塞函数
在 go 启动的子协程里调用 sleep()、file_get_contents()、PDO 查询等同步函数,会阻塞整个协程调度器,导致其他协程卡住——这不是“只卡自己”,而是卡住当前协程栈所属的整个协程调度线程。
正确做法是替换为协程版 I/O:
go(function () {
// ✅ 正确:协程友好的 sleep
co::sleep(1);
// ✅ 正确:协程 HTTP 客户端
$client = new Swoole\Coroutine\Http\Client('httpbin.org', 80);
$client->get('/delay/1');
echo $client->body;
});
- 所有网络、文件、数据库操作,必须使用 Swoole 提供的协程 API(如
co::sleep、Swoole\Coroutine\MySQL、Swoole\Coroutine\Redis) - 第三方库若没协程支持(如原生
curl_exec),需封装成defer+yield或改用Swoole\Coroutine\Socket手动实现 - 注意
exit()和die()在子协程里会终止整个进程,应改用return
协程退出后资源不会自动释放,需主动清理
协程结束时,PHP 不会自动回收它持有的资源(如未关闭的 Channel、未 close() 的 MySQL 连接、未 unset 的大数组),这些残留可能累积导致内存泄漏或连接耗尽。
- 务必在协程末尾显式关闭资源:
$mysql->close()、$channel->close() - 复杂逻辑建议用
try / finally确保清理逻辑执行:go(function () { $ch = new Swoole\Coroutine\Channel(); try { // ... do something } finally { $ch->close(); } }); - 协程内创建的普通变量(字符串、数字、小数组)会被自动回收,但资源型对象(句柄类)必须手动释放
close() 或误用一次引用传递,问题可能在线上压测时才暴露。










