PHP Fiber简单示例运行异常,请求排查问题原因
问题:PHP Fiber示例运行异常排查
我尝试运行一个简单的PHP Fiber示例,但运行结果不符合预期,首次使用Fiber,不清楚问题出在哪。
预期输出
iteration: 0 Fiber 1 is working! Fiber 2 is working! Fiber 3 is working (0)! (1 sec sleep) iteration: 1 Fiber 1 is working! Fiber 2 is working! Fiber 3 is working (1)! (1 sec sleep) iteration: 2 Fiber 1 is working! Fiber 2 is working! Fiber 3 completed! (1 sec sleep) iteration: 3 Fiber 1 is working! Fiber 2 is working! (1 sec sleep) ...
原代码
$fibers = []; // add fiber 1 fiber_await(new \Fiber(function(){ while(true){ echo "Fiber 1 is working!\n"; \Fiber::suspend(); } })); // add fiber 2 fiber_await(new \Fiber(function(){ while(true){ echo "Fiber 2 is working!\n"; \Fiber::suspend(); } })); // add fiber 3 fiber_await(new \Fiber(function(){ for($i=0; $i<2; $i++){ echo "Fiber 3 is working ($i)!\n"; \Fiber::suspend(); } echo "Fiber 3 completed!\n"; })); $j = 0; foreach($fibers as $i => &$pair){ echo "iteration: $j\n"; $parent = $pair[0]; if($parent->isTerminated()){ unset($fibers[$i]); } elseif($parent->isSuspended()){ $parent->resume(); } sleep(1); $j++; } function fiber_await(\Fiber $child): mixed{ $fibers[] = [\Fiber::getCurrent(), $child]; $child->start(); while(!$child->isTerminated()){ $child->resume(); \Fiber::suspend(); } return $child->getReturn(); }
实际输出
Fiber 1 is working! Fiber 1 is working! Fatal error: Uncaught FiberError: Cannot suspend outside of a fiber in Standard input code:46 Stack trace: #0 Standard input code(46): Fiber::suspend() #1 Standard input code(12): fiber_await(Object(Fiber)) #2 {main} thrown in Standard input code on line 46
问题分析
- 全局变量未正确引用:
fiber_await函数内的$fibers[]是局部变量,没有修改全局的$fibers数组,导致后续遍历的是空数组,逻辑完全走不下去。 - 主线程非法调用suspend:
fiber_await在主线程(非Fiber上下文)中调用\Fiber::suspend(),PHP规定只有在Fiber内部才能挂起自身,主线程作为初始执行上下文无法被挂起,直接抛出FiberError。 - 调度逻辑错误:原代码试图用
fiber_await来管理Fiber,但这个函数的设计逻辑完全错误,应该由主线程作为调度器,统一控制所有Fiber的唤醒和状态检查。
修正后的代码
$fibers = []; // 添加Fiber 1 $fibers[] = new \Fiber(function(){ while(true){ echo "Fiber 1 is working!\n"; \Fiber::suspend(); } }); // 添加Fiber 2 $fibers[] = new \Fiber(function(){ while(true){ echo "Fiber 2 is working!\n"; \Fiber::suspend(); } }); // 添加Fiber 3 $fibers[] = new \Fiber(function(){ for($i=0; $i<2; $i++){ echo "Fiber 3 is working ($i)!\n"; \Fiber::suspend(); } echo "Fiber 3 completed!\n"; }); // 启动所有Fiber foreach ($fibers as $fiber) { $fiber->start(); } $j = 0; while (!empty($fibers)) { echo "\niteration: $j\n"; // 遍历并过滤已终止的Fiber $fibers = array_filter($fibers, function(\Fiber $fiber) { if (!$fiber->isTerminated()) { if ($fiber->isSuspended()) { $fiber->resume(); } return true; } return false; }); echo "(1 sec sleep)\n"; sleep(1); $j++; }
修正说明
- 直接将所有Fiber实例存入全局
$fibers数组,避免作用域问题。 - 主线程作为调度器,先启动所有Fiber,然后在循环中逐个检查状态:未终止的Fiber如果处于挂起状态就唤醒它,已终止的则从数组中移除。
- 每次迭代完成后sleep 1秒,符合预期的输出节奏。
内容的提问来源于stack exchange,提问作者clarkk
相关产品推荐
相关产品推荐

