登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP Fiber 抛出异常后还能再次 resume 吗

来源:17golang原创

时间:2026-10-06 12:38:42 481浏览 收藏

能不能再次 resume(),不看“之前抛过异常”这一个动作,而看异常离开 Fiber 回调时的状态。异常被 Fiber 内部的 catch 接住,Fiber 仍可以在后续再次 Fiber::suspend(),这时可以继续 resume();异常没有被接住,Fiber 回调就以异常结束,Fiber 进入终止状态,之后再调用 resume() 只会得到 FiberError。

官方手册:https://www.php.net/manual/en/class.fiber.php

要点速览
  • Fiber::throw() 会把异常从当前 Fiber::suspend() 点注入 Fiber。
  • 内部捕获后重新挂起,才存在再次 resume() 的机会。
  • 先检查 isSuspended() 和 isTerminated(),不要靠猜测调用。

先看异常最终落在哪里

Fiber 有两个容易混淆的“抛异常”入口。回调内部直接 throw,或外部调用 $fiber->throw($exception),最终都要看异常有没有在 Fiber 的调用栈内被处理。只要异常穿出回调,官方定义的终止状态就成立;终止可能来自正常返回,也可能来自未捕获异常。

结果Fiber 状态后续动作
catch 接住并再次 suspend仍为 suspended可以 resume
异常离开回调terminated不能 resume
尚未 start 或正在运行不是可恢复点不要调用 resume
PHP Fiber::throw 将异常送入 suspend 点并由 catch 恢复的静态结构说明图
图1:PHP Fiber 的异常注入、捕获和再次挂起关系说明图,不是运行截图。

捕获异常后如何保留下一次 resume

下面的例子让 Fiber 先挂起,外部通过 throw() 把异常送到当前挂起点。catch 处理异常后再次挂起,外部才有第二次 resume() 的合法入口。

getMessage());
    }
});

// start() 返回第一个挂起点的值。
$first = $fiber->start();

// throw() 会在当前 suspend() 位置抛出异常,而不是让 Fiber 自动重启。
$second = $fiber->throw(new RuntimeException('temporary failure'));

// catch 分支再次 suspend,所以这里仍可合法 resume。
$fiber->resume('continue');

// 只有回调正常结束后,才把 Fiber 视为完成。
var_dump($fiber->isTerminated()); // true
?>

这里的关键不是把异常“重试”一次,而是异常被捕获后,回调仍然有未完成的执行路径。若 catch 中没有第二个 suspend(),回调会继续走到末尾,throw() 调用返回后 Fiber 就已经终止,再调用 resume() 仍然不成立。

未捕获异常时为什么不能恢复

未捕获异常代表 Fiber 的回调已经没有可继续的执行位置。可以把它当作一次失败的协程实例,而不是可重置的任务对象。需要重试时,应新建一个 Fiber,并把输入、重试次数和清理动作交给上层调度器。

start();
try {
    // resume() 继续执行后,未捕获异常会从这里冒出。
    $failed->resume();
} catch (Throwable $exception) {
    // 先记录原异常,再判断状态,禁止把同一实例当作可重试对象。
    error_log($exception->getMessage());
}

if ($failed->isTerminated()) {
    // 真正的重试应创建 new Fiber,而不是再次调用 $failed->resume()。
    $retry = new Fiber(static fn (): string => 'retry-ok');
    $retry->start();
}
?>

isTerminated() 在正常返回和抛出异常后都会是 true。而 getReturn() 只适合读取正常返回值;Fiber 因异常终止时,它不能替代异常处理,也不能把失败实例变回 suspended。

PHP Fiber suspended、running、terminated 状态与 resume 边界的静态关系图
图2:PHP Fiber 状态检查与 resume 边界说明图,不是实际运行结果。

调用 resume 前的检查清单

在事件循环、任务池或请求编排器里,把恢复动作收敛到一个小函数更安全。进入恢复前至少判断:Fiber 已启动、当前确实挂起、没有被上一次异常终止。若状态不满足,就返回任务失败或创建新的 Fiber,而不是吞掉 FiberError。

检查说明处理建议
isStarted()未启动不能 resume先 start
isSuspended()只有挂起点可被恢复调用 resume 或 throw
isTerminated()正常返回或异常都表示结束读取结果或新建 Fiber

常见问题

Fiber::throw() 后 catch 住异常就一定能 resume 吗?

不一定。catch 之后如果回调直接结束,Fiber 已终止;只有后续路径再次执行 Fiber::suspend(),外部才有新的恢复点。

Fiber::resume() 报 FiberError 是异常没捕获吗?

常见原因是 Fiber 未启动、正在运行或已经终止。先结合 isStarted()、isRunning()、isSuspended() 和 isTerminated() 判断,不要只看错误文字。

失败后怎样实现重试?

保留原异常和输入,完成资源清理后创建新的 Fiber。不要复用已经 terminated 的对象,也不要用反射或状态字段伪造恢复点。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>