PHP 8.1 中空数组本身定义简单,关键在于如何安全有序地在多个 Fiber 间共享或累积数据;应避免跨 Fiber 直接共享可变数组,推荐各 Fiber 返回结构化结果,由主流程用空数组统一归并,并确保 Fiber 终止后再读取。

PHP 8.1 中定义空数组本身很简单,但配合 Fiber 实现异步数据收集时,关键不在“怎么定义空数组”,而在于**如何安全、有序地在多个 Fiber 之间共享或累积数据**。空数组常作为初始容器,用于逐步收集 I/O 操作结果(如并发 HTTP 请求、数据库查询返回值),但直接在 Fiber 外部共享变量存在竞态风险——Fiber 是协作式调度,虽不抢占,但执行时机由开发者控制,若多个 Fiber 同时写入同一数组且无同步机制,仍可能因 resume 顺序导致覆盖或遗漏。
为什么不能直接用全局空数组收集?
看似可行的写法:
```php$results = [];
$fiber1 = new Fiber(function() use (&$results) {
sleep(1); // 模拟阻塞 I/O
$results[] = 'data1';
});
$fiber2 = new Fiber(function() use (&$results) {
sleep(1);
$results[] = 'data2';
});
$fiber1->start();
$fiber2->start();
// 等待全部完成后再读取 $results
```
问题在于:sleep() 不是 Fiber 友好操作,它会阻塞整个线程,使 Fiber 失去“异步”意义;且上述代码未做任何调度协调,两个 Fiber 实际是串行启动、各自阻塞,$results 虽能收集到数据,但并未体现 Fiber 的协作优势。
正确做法:用 Fiber 控制流程,空数组作为局部累积容器
真正发挥 Fiber 价值的方式,是让每个 Fiber 自己管理临时数据,并通过 resume/suspend 与主流程通信,最终由主流程统一归并。空数组应在 Fiber 内部或主流程中初始化,避免跨 Fiber 共享可变引用。
立即学习“PHP免费学习笔记(深入)”;
- 每个 Fiber 返回结构化结果(如 ['id' => 1, 'data' => $value]),而非直接修改外部数组
- 主流程用空数组接收所有 Fiber 的返回值:$allResults = [];
- 按需启动 Fiber,挂起等待 I/O,恢复后将结果 push 到 $allResults
- 示例逻辑:
$allResults = [];
// 模拟非阻塞 I/O 封装(实际需配合事件循环或扩展)
$fetch = function(string $url): string {
Fiber::suspend("waiting for {$url}"); // 暂停,通知主流程可干别的
return "result from {$url}"; // 恢复后返回数据
};
$fiber1 = new Fiber(fn() => $fetch('https://api.a'));
$fiber2 = new Fiber(fn() => $fetch('https://api.b'));
// 启动并收集
$fiber1->start();
$fiber2->start();
// 主流程模拟“轮询 I/O 完成”
usleep(1000); // 简化示意,真实场景用事件循环
$allResults[] = $fiber1->resume('done');
$allResults[] = $fiber2->resume('done');
var_dump($allResults); // 安全、顺序可控
```
进阶:用 Fiber 配合协程式 I/O 扩展(如 Swoole)
Fiber 本身不提供非阻塞 I/O,必须与支持协程的底层扩展结合才能实现真正的异步数据收集。此时空数组仍是理想的结果容器,但使用更自然:
- 在 Swoole 协程环境中,co::sleep() 或 co::httpGet() 会自动触发 Fiber 挂起,无需手动调用 suspend()
- 每个请求 Fiber 可独立运行,主流程用空数组汇总:
$results = [];
go(function() use (&$results) {
$res1 = co::httpGet('https://httpbin.org/delay/1');
$results[] = ['url' => '1', 'body' => $res1['body']];
});
go(function() use (&$results) {
$res2 = co::httpGet('https://httpbin.org/delay/1');
$results[] = ['url' => '2', 'body' => $res2['body']];
});
// 等待所有协程结束(Swoole 自动调度)
Swoole\Event::wait();
var_dump($results); // 并发完成,空数组被安全填充
```
关键提醒:Fiber 状态与数组填充时机
别在 Fiber 还未 resume 就读取空数组——它仍是空的。务必确认 Fiber 已终止($fiber->isTerminated() === true)再取结果。常用判断方式:
- 调用 $fiber->resume() 后检查返回值是否为预期结果
- 用 $fiber->getReturn() 获取闭包最终返回值(仅在 terminated 后有效)
- 搭配 $fiber->isSuspended() 判断是否还需 resume
空数组在这里只是起点,真正重要的是你用什么逻辑把数据“送进去”。Fiber 不改变数组语法,但彻底改变了你组织异步数据流的方式。



















