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 异步编程的最佳时机!
发布评论
热门评论区: