/ PHP  Fibers  协程  异步编程  PHP8.1  性能优化  事件循环  并发 

PHP 8.1 Fibers协程实战:构建高性能异步任务调度器


封面

什么是 PHP Fibers(协程)?

PHP 8.1 正式引入了 Fibers(纤程/协程),这是 PHP 并发编程领域一次重大突破。Fibers 提供了一种轻量级的协作式多任务机制,允许开发者暂停和恢复代码执行,而无需借助操作系统线程或多进程。

与传统的 Generator 类似,Fibers 可以在执行过程中被挂起,但它更强大:Fibers 可以从调用栈的任意深度暂停,而不仅仅是直接在 yield 语句处。这使得构建异步框架、事件循环和协程调度器成为可能。

<?php
// 创建一个基本的 Fiber
$fiber = new Fiber(function(): void {
    $value = Fiber::suspend('first suspend');
    echo "Fiber received: " . $value . PHP_EOL;
    Fiber::suspend('second suspend');
    echo "Fiber resumed again" . PHP_EOL;
});

$result1 = $fiber->start();         // 输出: first suspend
echo "Main got: $result1" . PHP_EOL;

$result2 = $fiber->resume('hello'); // 输出: Fiber received: hello
echo "Main got: $result2" . PHP_EOL;

$fiber->resume(); // 输出: Fiber resumed again

Fibers 核心 API 详解

PHP Fibers 的 API 设计十分简洁,主要包含以下方法:

  • new Fiber(callable $callback):创建一个新的 Fiber 实例,传入要执行的回调函数
  • Fiber::start(...$args):启动 Fiber,传入初始参数,返回 Fiber::suspend() 的值
  • Fiber::resume(mixed $value):从上次 suspend 处恢复执行,传入值给 suspend 调用方
  • Fiber::suspend(mixed $value):暂停当前 Fiber,将值传递给 resume/start 的调用方
  • Fiber::isStarted():检查 Fiber 是否已启动
  • Fiber::isRunning():检查 Fiber 当前是否正在运行
  • Fiber::isSuspended():检查 Fiber 是否处于挂起状态
  • Fiber::isTerminated():检查 Fiber 是否已终止
  • Fiber::getReturn():获取 Fiber 的返回值(必须在 terminated 后调用)
<?php
$fiber = new Fiber(function(): string {
    echo "Step 1" . PHP_EOL;
    Fiber::suspend();
    echo "Step 2" . PHP_EOL;
    Fiber::suspend();
    echo "Step 3" . PHP_EOL;
    return "done";
});

$fiber->start();
var_dump($fiber->isStarted());    // bool(true)
var_dump($fiber->isSuspended());  // bool(true)
var_dump($fiber->isTerminated()); // bool(false)

$fiber->resume();
$fiber->resume();
var_dump($fiber->isTerminated()); // bool(true)
echo $fiber->getReturn() . PHP_EOL; // done

构建轻量级事件循环调度器

Fibers 最重要的应用场景是构建事件循环。下面演示一个简单但功能完整的任务调度器,可以并发执行多个异步任务:

<?php
class EventLoop
{
    /** @var Fiber[] */
    private array $fibers = [];

    public function add(callable $callback): void
    {
        $this->fibers[] = new Fiber($callback);
    }

    public function run(): void
    {
        // 启动所有 Fiber
        foreach ($this->fibers as $fiber) {
            $fiber->start();
        }

        // 循环直到所有 Fiber 终止
        while (true) {
            $allDone = true;
            foreach ($this->fibers as $fiber) {
                if (!$fiber->isTerminated()) {
                    $allDone = false;
                    if ($fiber->isSuspended()) {
                        $fiber->resume();
                    }
                }
            }
            if ($allDone) break;
        }
    }
}

// 使用示例
$loop = new EventLoop();

$loop->add(function(): void {
    echo "[Task 1] 开始处理请求..." . PHP_EOL;
    Fiber::suspend(); // 模拟 IO 等待
    echo "[Task 1] IO 完成,处理数据..." . PHP_EOL;
    Fiber::suspend();
    echo "[Task 1] 完成" . PHP_EOL;
});

$loop->add(function(): void {
    echo "[Task 2] 开始查询数据库..." . PHP_EOL;
    Fiber::suspend();
    echo "[Task 2] 查询完毕,格式化结果..." . PHP_EOL;
    Fiber::suspend();
    echo "[Task 2] 完成" . PHP_EOL;
});

$loop->run();

输出结果展示了两个任务交替执行,而非顺序阻塞执行——这正是协程的核心价值。

与 ReactPHP / Swoole 的配合使用

Fibers 本身只是底层机制,在实际项目中通常配合成熟的异步框架使用。

  • ReactPHP:从 v1.2.0 起全面支持 Fibers,可以用同步写法调用异步 API
  • Swoole:内置 Coroutine 与 PHP Fibers 深度集成,性能更强
  • Amp:v3.0 以 Fibers 为核心重新设计,提供更友好的异步 API
  • Revolt:专为 PHP Fibers 设计的事件循环底层库,被多个框架采用
<?php
// 使用 Amp v3 的 Fibers 风格
use Amp\Future;
use function Amp\async;
use function Amp\await;

// 并发发起多个 HTTP 请求
$futures = [
    async(fn() => fetchUrl('https://api.example.com/users')),
    async(fn() => fetchUrl('https://api.example.com/orders')),
    async(fn() => fetchUrl('https://api.example.com/products')),
];

// 等待所有请求完成(并发执行)
[$users, $orders, $products] = Future\await($futures);
echo "获取到 " . count($users) . " 个用户" . PHP_EOL;

实践建议与性能对比

在实际项目中使用 Fibers 时,有几点需要特别注意:

  • 适合 IO 密集型场景:数据库查询、HTTP 调用、文件读写——这些 IO 等待时间可被其他 Fiber 利用
  • 不适合 CPU 密集型:Fibers 是协作式(非抢占式),CPU 密集计算会阻塞整个事件循环
  • 错误处理:Fiber 内抛出的异常会在 resume()start() 处传播到调用方
  • 内存开销:每个 Fiber 需要约 8KB 栈空间,远低于线程(通常 1-8MB)
  • PHP 版本要求:Fibers 需要 PHP 8.1+,8.3 版本进一步优化了性能

根据基准测试,在高并发 HTTP 服务场景中,使用 Fibers + ReactPHP 的 PHP 应用吞吐量可达传统同步模式的 5-10 倍,内存占用仅增加 20%-30%,是提升 PHP 应用性能的高性价比方案。

如果你的项目运行在 PHP 8.1+ 环境,现在就是拥抱 Fibers 异步编程的最佳时机!

发布评论

热门评论区: