本文目录导读:

PHP Fiber(纤程)是 PHP 8.1 引入的协程实现,允许你在单线程内实现类似多线程的并发执行,我来详细介绍它的用法。
基本概念
Fiber 是可中断和可恢复的函数,类似于生成器,但更强大:
// 创建一个 Fiber
$fiber = new Fiber(function() {
echo "Fiber 开始执行\n";
// 暂停执行,返回控制权给调用者
Fiber::suspend("暂停值");
echo "Fiber 恢复执行\n";
return "完成值";
});
// 启动 Fiber
$value = $fiber->start();
echo "收到: $value\n";
// 恢复 Fiber 执行
$value = $fiber->resume();
echo "收到: $value\n";
基本用法示例
1 简单的协程切换
function task1() {
for ($i = 1; $i <= 3; $i++) {
echo "任务1 - 步骤$i\n";
Fiber::suspend($i);
}
return "任务1完成";
}
function task2() {
for ($i = 1; $i <= 3; $i++) {
echo "任务2 - 步骤$i\n";
Fiber::suspend($i * 10);
}
return "任务2完成";
}
// 创建两个 Fiber
$fiber1 = new Fiber('task1');
$fiber2 = new Fiber('task2');
// 交替执行
$fiber1->start();
$fiber2->start();
while (!$fiber1->isTerminated() || !$fiber2->isTerminated()) {
if (!$fiber1->isTerminated()) {
echo "从任务1收到: " . $fiber1->resume() . "\n";
}
if (!$fiber2->isTerminated()) {
echo "从任务2收到: " . $fiber2->resume() . "\n";
}
}
2 数据传递
$fiber = new Fiber(function($param) {
echo "开始执行,接收参数: $param\n";
$value = Fiber::suspend("请求数据");
echo "收到数据: $value\n";
$value = Fiber::suspend("再次请求");
echo "再次收到: $value\n";
return "最终结果";
});
// 启动并传入初始参数
$result = $fiber->start("初始参数");
echo "第一次暂停: $result\n";
// 恢复并传入数据
$result = $fiber->resume("数据1");
echo "第二次暂停: $result\n";
$result = $fiber->resume("数据2");
echo "返回结果: $result\n";
实用场景示例
1 模拟异步 IO
class AsyncFileReader {
public function readFiles(array $files) {
$fibers = [];
// 创建所有 Fiber
foreach ($files as $index => $file) {
$fibers[$index] = new Fiber(function($file) {
// 模拟异步读取
for ($i = 1; $i <= 100; $i++) {
if ($i % 25 === 0) {
Fiber::suspend($i / 25 * 25);
}
// 模拟读取数据
usleep(1000);
}
return file_get_contents($file);
});
$fibers[$index]->start($file);
}
// 收集结果
$results = [];
$completed = 0;
$maxFiles = count($files);
while ($completed < $maxFiles) {
foreach ($fibers as $index => $fiber) {
if (!$fiber->isTerminated()) {
$progress = $fiber->resume();
echo "文件{$index}: {$progress}%\n";
if ($fiber->isTerminated()) {
$results[$files[$index]] = $fiber->getReturn();
$completed++;
}
}
}
}
return $results;
}
}
2 简单的任务调度器
class Scheduler {
private array $tasks = [];
public function add(callable $task) {
$this->tasks[] = new Fiber($task);
}
public function run() {
while (!empty($this->tasks)) {
foreach ($this->tasks as $key => $fiber) {
if ($fiber->isStarted() && $fiber->isTerminated()) {
unset($this->tasks[$key]);
continue;
}
if (!$fiber->isStarted()) {
$fiber->start();
} else {
$fiber->resume();
}
// 如果任务已完成,移除
if ($fiber->isTerminated()) {
unset($this->tasks[$key]);
}
}
}
}
}
// 使用调度器
$scheduler = new Scheduler();
$scheduler->add(function() {
for ($i = 0; $i < 5; $i++) {
echo "任务A: $i\n";
Fiber::suspend();
}
});
$scheduler->add(function() {
for ($i = 0; $i < 3; $i++) {
echo "任务B: $i\n";
Fiber::suspend();
}
});
$scheduler->run();
3 模拟并发请求
class ConcurrentHttpClient {
public function request(array $urls) {
$fibers = [];
foreach ($urls as $index => $url) {
$fibers[$index] = new Fiber(function($url) {
// 模拟发送请求
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 分段执行请求
$result = '';
for ($i = 0; $i < 10; $i++) {
usleep(100000); // 100ms
$result .= "chunk$i-";
if ($i < 9) {
Fiber::suspend();
}
}
curl_close($ch);
return $result;
});
$fibers[$index]->start($url);
}
$results = [];
foreach ($fibers as $index => $fiber) {
while (!$fiber->isTerminated()) {
$fiber->resume();
}
$results[$urls[$index]] = $fiber->getReturn();
}
return $results;
}
}
重要方法和状态
$fiber = new Fiber(function() {
echo "执行中\n";
Fiber::suspend();
echo "继续执行\n";
});
// 状态检查
echo $fiber->isStarted() ? "已启动\n" : "未启动\n"; // false
echo $fiber->isSuspended() ? "已暂停\n" : "未暂停\n"; // false
echo $fiber->isTerminated() ? "已终止\n" : "未终止\n"; // false
$fiber->start();
echo $fiber->isSuspended() ? "已暂停\n" : "未暂停\n"; // true
$fiber->resume();
echo $fiber->isTerminated() ? "已终止\n" : "未终止\n"; // true
// 获取返回值
$result = $fiber->getReturn();
异常处理
$fiber = new Fiber(function() {
try {
// 业务逻辑
throw new Exception("自定义异常");
} catch (Exception $e) {
echo "在 Fiber 内捕获: " . $e->getMessage() . "\n";
Fiber::suspend("异常处理中");
throw $e; // 重新抛出
}
});
try {
$fiber->start();
$fiber->resume();
} catch (Exception $e) {
echo "在外部捕获: " . $e->getMessage() . "\n";
}
// 或者使用 throw() 方法
$fiber2 = new Fiber(function() {
echo "执行中\n";
Fiber::suspend("暂停");
echo "继续执行\n";
});
$fiber2->start();
$fiber2->throw(new Exception("外部抛入异常"));
与生成器的对比
// 生成器方式
function generatorTask() {
for ($i = 1; $i <= 5; $i++) {
yield $i; // 只能传递数据,不能接收外部数据
}
}
// Fiber 方式
$fiberTask = new Fiber(function() {
for ($i = 1; $i <= 5; $i++) {
$input = Fiber::suspend($i); // 可以双向传递数据
echo "收到输入: $input\n";
}
});
$fiberTask->start();
$fiberTask->resume("输入数据1");
注意事项
- 内存使用:每个 Fiber 都有独立的调用栈,不要创建过多
- 性能:Fiber 切换比线程切换快,但不是在真并行
- 使用场景:适合 IO 密集型任务,不适合 CPU 密集计算
- 限制:不能在 Fiber 中调用
exit或die
Fiber 提供了强大的协程能力,但实际使用时需要根据场景选择合适的异步方案,对于简单的并行需求,可以考虑使用 yield 生成器;对于复杂的异步逻辑,Fiber 会是更好的选择。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播