PHP 怎么协程等待组

wen PHP项目 1

本文目录导读:

PHP 怎么协程等待组

  1. 使用 Swoole 的 Coroutine\WaitGroup
  2. 使用 Swoole 的 Channel 模拟
  3. 使用 Fiber(PHP 8.1+)实现
  4. 使用 ReactPHP 的 Promise
  5. 自定义简单的协程等待组
  6. 建议

在 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";
});

建议

  1. 生产环境推荐使用 Swoole,它是最成熟、性能最好的 PHP 协程方案
  2. PHP 8.1+ 可以使用 Fiber,但功能相对基础
  3. 记得处理异常,确保 done() 一定被调用(使用 finally
  4. 避免在协程中执行阻塞操作,否则会阻塞整个事件循环

选择哪种方案取决于你的项目需求和运行环境。

抱歉,评论功能暂时关闭!