协程异常严格隔离,无法跨协程传播;每个协程须独立处理异常,exit()仅终止当前协程;推荐用try-catch兜底+trace_id日志,或channel显式传递错误。

协程异常不会跨协程传播,Swoole 5 中不存在“向上捕获”机制——每个协程必须独立处理自己的异常,嵌套创建的子协程抛出的异常无法被父协程的 try-catch 捕获。
协程异常隔离是强制设计原则
无论协程是通过 go()、Co\run() 还是嵌套调用 go() 创建,只要属于不同协程上下文,异常就严格隔离:
- 父协程中 catch 的 Throwable 仅覆盖当前协程内 throw 或 exit 的异常
- 子协程中未捕获的 RuntimeException、ExitException 等,只终止该子协程本身
- 父协程继续运行,不受影响;也不会收到任何通知或回调
- Worker 进程不会因此崩溃,但若高频发生未捕获异常,可能掩盖逻辑缺陷
exit() 在协程中的特殊行为
使用 exit() 会触发 Swoole\ExitException,但它仍是协程局部异常:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 只能在当前协程内 try-catch,不能由外层协程捕获
- exit(123) → 抛出 ExitException,status=123,但仅该协程退出
- 多个嵌套 go() 中分别 exit,彼此无感知,也无状态同步
正确做法:每个协程都应有兜底异常处理
避免漏捕获导致静默失败,推荐在协程入口统一包裹基础错误处理:
- 用 try/catch 包裹整个协程函数体,至少记录日志
- 结合 trace_id(如 Co\Trace::getTraceId())和 cid(Co::getCid())打点,便于定位
- 对关键业务协程,可主动向 Channel 或 Logger 发送错误事件,实现“可观测式上报”
- 不依赖“传递异常”,而是通过结构化消息(如 JSON 错误包)显式通信
替代方案:用 Channel 实现错误通知
若需让父协程感知子协程失败,应改用通信而非异常传递:
- 父协程创建 unbuffered Channel(或带容量的 channel)
- 子协程执行完毕后,send(['error' => $e->getMessage(), 'code' => 500])
- 父协程 recv() 等待结果,统一判断是否出错
- 这种方式符合 Swoole 协程通讯规范,安全且可控










