swoole 中 Coroutine\WaitGroup 和channel区别和使用场景
一、一句话区分
- WaitGroup = “计数器闩锁”:事先知道要启动多少子协程,主协程等它们 全部跑完 再往下走;不传递数据,只同步“结束”事件。
- Channel = “内存队列”:事先 不知道 子协程数量或需要收发数据,协程间
push/pop
消息;既能同步又能通信。
二、WaitGroup 详解
- 原理:内部维护一个
count
;add(n)
让count+=n
;每个子协程done()
让count-=1
;wait()
在count>0
时阻塞,底层就是Channel->pop()
。 - 场景:并发任务数量确定,只需“全部完成”信号。
- 示例:并发爬 3 个网页,全部返回后统一输出。
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;run(function () {$wg = new WaitGroup();$results = [];foreach (['baidu', 'taobao', 'qq'] as $site) {$wg->add(); // 计数 +1Coroutine::create(function () use ($wg, &$results, $site) {$results[$site] = file_get_contents("https://$site.com");$wg->done(); // 计数 -1});}$wg->wait(); // 全部 done 才继续echo "全部抓取完成,共 " . count($results) . " 条\n";
});
三、Channel 详解
- 原理:协程级多生产者-多消费者队列;满时
push()
自动让出 CPU,空时pop()
自动让出 CPU;可设置容量,也可传任意 PHP 值(零拷贝)。 - 场景:
- 数据流管道:生产者不断生成,消费者一边处理一边消费;
- 并发数量未知:用“结束哨兵”通知消费者退出;
- 连接池/任务池:把可用连接或任务塞进 Channel,工作协程
pop()
获取。
- 示例:未知数量任务流式处理。
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;run(function () {$chan = new Channel(10); // 缓冲区 10// 3 个生产者for ($i = 0; $i < 3; $i++) {Coroutine::create(function () use ($chan, $i) {foreach (range(1, 5) as $v) {$chan->push("任务-$i-$v");Coroutine::sleep(0.1); // 模拟 IO}$chan->push(null); // 本生产者结束哨兵});}// 2 个消费者for ($i = 0; $i < 2; $i++) {Coroutine::create(function () use ($chan, $i) {while (1) {$task = $chan->pop(); // 无任务自动挂起if ($task === null) break;echo "消费者 $i 处理 $task\n";}});}
});
四、对照表
维度 | WaitGroup | Channel |
---|---|---|
核心功能 | 同步“全部完成” | 消息队列 + 同步 |
数据传递 | ❌ | ✅(任意 PHP 值) |
子协程数量 | 必须事先知道 | 可动态/未知 |
典型场景 | 并发请求、批量写入 | 流式处理、连接池、任务队列 |
实现原理 | 内部用 Channel 做计数阻塞 | 底层无锁队列 + yield/resume |
五、一句话总结
- 只要“等全部跑完”→ WaitGroup;
- 还要“收发数据”或“数量未知”→ Channel;
- WaitGroup 底层就是 对 Channel 的轻量级封装,二者常配合使用。