fibers 本身不提供非阻塞 i/o,需配合非阻塞系统调用或事件循环;新建 fiber 处于 created 状态,须先调用 start() 才能 resume;suspend() 返回值传给外部,resume($value) 参数传入 fiber 内部;fread 不阻塞需三步:设流为非阻塞、检查空读并 suspend、由事件循环通知可读。

Fibers 不是开箱即用的非阻塞 I/O,它只是提供了挂起/恢复执行流的能力;要真正实现非阻塞,你必须配合非阻塞系统调用(如 stream_set_blocking(false))或事件循环(如 ReactPHP、Swoole),否则 fread、file_get_contents 这类函数照旧会阻塞主线程。
为什么 new Fiber(...) 后不能直接 resume?
Fiber 创建后处于 CREATED 状态,必须先调用 start() 才能进入可恢复的 SUSPENDED 状态。直接对未启动的 Fiber 调用 resume() 会抛出 FiberException: Cannot resume a fiber that has not been started。
-
start()是单次操作:它启动 Fiber 并运行到第一个Fiber::suspend(),返回该 suspend 的值 -
resume()只对已挂起(SUSPENDED)或已终止(TERMINATED)的 Fiber 有效;对已终止的 Fiber resume 会报Cannot resume a terminated fiber - 检查状态可用
$fiber->isTerminated()或$fiber->isSuspended(),别依赖 try/catch 盲调
Fiber::suspend() 返回值和 resume() 参数怎么传?
Fiber::suspend() 的参数是**返回给调用方的值**,不是传给 Fiber 自身的;而 resume($value) 的参数才是**传入 Fiber 内部、作为 suspend() 调用的返回值**。这个双向流向容易搞反。
- 示例中
$value = Fiber::suspend('ready'):主程序收到字符串'ready',而$value在 Fiber 内部是resume()传进来的值 - 如果
resume()没传参,$value就是null;不要假设它自动继承上次值 - 类型不匹配不会报错,但逻辑可能断裂——比如期望
string却收到int,需自行校验
怎样让 fread 不阻塞?光用 Fiber 不行
单纯把 fread() 包进 Fiber 里,不改底层 IO 模式,它依然会卡住整个线程。关键在三步联动:
- 打开流时必须设为非阻塞:
stream_set_blocking($stream, false) - 读取时检查返回值:
$chunk = fread($stream, 100)可能返回false(出错)或空字符串''(暂无数据),此时应Fiber::suspend()让出控制权 - 需要外部机制通知“数据可读了”,比如用
stream_select()轮询,或交给 ReactPHP 的LoopInterface调度 - 错误示例:
while (!feof($stream)) { fread($stream, 100); }——feof()在非阻塞模式下不可靠,且没处理空读场景
常见陷阱:Fiber 生命周期与异常传递
Fiber 终止后无法再 resume,但异常行为比想象中更隐蔽:
- Fiber 内未捕获的异常会直接终止它,并向上抛给
resume()调用方(不是原始start()处) -
Fiber::throw()可向挂起的 Fiber 注入异常,但仅当它处于SUSPENDED状态;对已终止 Fiber throw 会静默失败 - Fiber 栈是隔离的,但变量作用域仍遵循 PHP 函数闭包规则——
use ($var)捕获的是值拷贝,不是引用,除非显式加& - 调试时
debug_backtrace()在 Fiber 内看到的是 Fiber 栈,不是主栈;别指望__FILE__和__LINE__总指向你预期的位置
真正难的不是写 suspend/resume,而是设计调度器:谁来决定何时 resume 哪个 Fiber?什么时候该轮询 stream?这些逻辑一旦散落在各处,就退化成更难维护的回调地狱。Fiber 把控制权交还给你,也把调度责任一并移交了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











