本文目录导读:

- 使用 Swoole 的 Coroutine\WaitGroup
- 使用 Swoole 的 Channel 模拟
- 使用 Fiber(PHP 8.1+)实现
- 使用 ReactPHP 的 Promise
- 自定义简单的协程等待组
- 建议
在 PHP 中实现协程等待组(类似 Go 的 sync.WaitGroup),可以使用 Swoole 或 Fiber 扩展,以下是几种实现方式:
使用 Swoole 的 Coroutine\WaitGroup
Swoole 提供了内置的 WaitGroup 类:
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
$waitGroup = new WaitGroup();
// 启动 3 个协程
for ($i = 0; $i < 3; $i++) {
$waitGroup->add(); // 计数器 +1
Coroutine::create(function () use ($waitGroup, $i) {
try {
// 模拟异步任务
Coroutine::sleep(rand(1, 3));
echo "Task {$i} completed\n";
} finally {
$waitGroup->done(); // 计数器 -1
}
});
}
// 等待所有协程完成
$waitGroup->wait();
echo "All tasks completed\n";
使用 Swoole 的 Channel 模拟
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
$channel = new Channel(10);
$taskCount = 3;
for ($i = 0; $i < $taskCount; $i++) {
Coroutine::create(function () use ($channel, $i) {
Coroutine::sleep(rand(1, 3));
echo "Task {$i} completed\n";
$channel->push(true); // 发送完成信号
});
}
// 等待所有任务完成
for ($i = 0; $i < $taskCount; $i++) {
$channel->pop();
}
echo "All tasks completed\n";
使用 Fiber(PHP 8.1+)实现
<?php
class FiberWaitGroup {
private int $count = 0;
private array $waiters = [];
public function add(int $delta = 1): void {
$this->count += $delta;
}
public function done(): void {
$this->count--;
if ($this->count === 0) {
// 唤醒所有等待的 Fiber
foreach ($this->waiters as $waiter) {
if (!$waiter->isTerminated()) {
$waiter->resume();
}
}
$this->waiters = [];
}
}
public function wait(): void {
if ($this->count === 0) {
return;
}
$this->waiters[] = Fiber::this();
Fiber::suspend();
}
}
// 使用示例
function asyncTask(int $id, FiberWaitGroup $wg): void {
Fiber::suspend(); // 模拟异步操作
echo "Task {$id} completed\n";
$wg->done();
}
$wg = new FiberWaitGroup();
$fibers = [];
for ($i = 0; $i < 3; $i++) {
$wg->add();
$fiber = new Fiber(function () use ($i, $wg) {
asyncTask($i, $wg);
});
$fibers[] = $fiber;
$fiber->start();
}
// 等待所有 Fiber 完成
$wg->wait();
echo "All tasks completed\n";
使用 ReactPHP 的 Promise
虽然不是协程,但也能实现类似功能:
<?php
require 'vendor/autoload.php';
use React\EventLoop\Factory;
use React\Promise\Promise;
$loop = Factory::create();
function asyncTask(int $id, $loop): Promise {
return new Promise(function ($resolve) use ($id, $loop) {
$loop->addTimer(rand(1, 3), function () use ($id, $resolve) {
echo "Task {$id} completed\n";
$resolve(true);
});
});
}
$tasks = [];
for ($i = 0; $i < 3; $i++) {
$tasks[] = asyncTask($i, $loop);
}
\React\Promise\all($tasks)->then(function () {
echo "All tasks completed\n";
$this->loop->stop();
});
$loop->run();
自定义简单的协程等待组
<?php
use Swoole\Coroutine;
class SimpleWaitGroup {
private int $count = 0;
private array $waitingFns = [];
public function add(): void {
$this->count++;
}
public function done(): void {
$this->count--;
if ($this->count === 0 && !empty($this->waitingFns)) {
foreach ($this->waitingFns as $fn) {
$fn();
}
$this->waitingFns = [];
}
}
public function wait(callable $onComplete): void {
if ($this->count === 0) {
$onComplete();
return;
}
$this->waitingFns[] = $onComplete;
}
}
// 使用示例
$wg = new SimpleWaitGroup();
$wg->add();
Coroutine::create(function () use ($wg) {
Coroutine::sleep(1);
echo "Task 1\n";
$wg->done();
});
$wg->add();
Coroutine::create(function () use ($wg) {
Coroutine::sleep(2);
echo "Task 2\n";
$wg->done();
});
$wg->wait(function () {
echo "All tasks completed\n";
});
建议
- 生产环境推荐使用 Swoole,它是最成熟、性能最好的 PHP 协程方案
- PHP 8.1+ 可以使用 Fiber,但功能相对基础
- 记得处理异常,确保
done()一定被调用(使用finally) - 避免在协程中执行阻塞操作,否则会阻塞整个事件循环
选择哪种方案取决于你的项目需求和运行环境。