waitgroup 无法获取协程返回值,需用 channel 或引用变量收集结果;channel 更安全推荐,需设容量避免阻塞;wait() 后不可直接读共享变量,应确保写入完成再调 done()。

WaitGroup 在 Swoole 中不能直接等待协程结果
因为 Swoole\Coroutine\WaitGroup 本身不保存协程返回值,它只做计数和阻塞等待——类似 Go 的 sync.WaitGroup,但没有 Go 那种隐式返回捕获能力。你调用 $wg->add(1) 和 $wg->done(),只能知道“某个协程结束了”,但拿不到它的 return 值。
要用 Channel 或引用变量收集协程结果
最常用、最稳妥的方式是配合 Swoole\Coroutine\Channel:每个协程把结果 $chan->push($result) 进去,主协程按顺序 $chan->pop() 拿;或者用引用数组(需注意并发写入安全)。
-
Channel天然线程/协程安全,推荐用于结果收集,尤其结果类型不一致或需要保序时 - 若所有协程都写同一个数组,必须用
co::sleep(0)让出调度,或改用Channel,否则可能漏写或覆盖 -
Channel容量建议设为协程总数(如new Channel($n)),避免push阻塞影响并发效率
// 示例:用 Channel 收集 3 个协程的整数结果
$chan = new Swoole\Coroutine\Channel(3);
$wg = new Swoole\Coroutine\WaitGroup();
<p>for ($i = 0; $i add();
go(function () use ($i, $chan, $wg) {
co::sleep(0.1);
$chan->push($i * 2);
$wg->done();
});
}</p><p>$wg->wait(); // 等所有协程结束
$results = [];
while ($chan->length() > 0) {
$results[] = $chan->pop();
}
// $results 现在包含 [0, 2, 4]</p>
别在 WaitGroup::wait() 后直接读共享变量
很多人误以为只要 $wg->wait() 返回,所有协程就“彻底执行完毕”,可以安全读取外部变量——其实不然。Swoole 协程调度是抢占式的,wait() 只保证 done() 已被调用,但协程栈可能还没完全退出,极小概率导致变量读取不完整(尤其涉及对象属性、引用赋值时)。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 如果必须用共享变量(比如
&$results),确保每个协程内部完成写入后才调$wg->done() - 避免在协程中异步操作(如
go(...)、defer)后再写变量,这类延迟执行会脱离WaitGroup的控制范围 - 更可靠的做法仍是 Channel +
pop(),它天然串行化结果交付时机
PHP 8.1+ 可考虑 Fiber + 自定义 WaitGroup(不推荐生产)
原生 Fiber 能拿到返回值,理论上可自己封装带结果的等待器,但 Swoole 的 go 是基于 Fiber 的增强实现,直接混用反而增加调试难度。目前没有稳定、轻量、被广泛验证的第三方 ResultWaitGroup 包。
- 不要为了“看起来更像 Go”而强行抽象一层,增加不可控调度路径
- 现有
Channel+WaitGroup组合已足够清晰,且性能无损 - 真正复杂的结果聚合(如超时、错误合并、限流)建议用
Swoole\Coroutine\WaitGroup+Swoole\Coroutine\Channel+ 显式try/catch手动编排
实际用的时候,Channel 的 push/pop 开销几乎可以忽略,但它把“结果交付”这个动作显式化了——这点比任何黑盒等待逻辑都重要。










