parallel是swoole中基于多进程的并行机制,非协程并发;子进程完全隔离、需手动错误处理、返回无序、不可复用父进程资源,不适用于有依赖的串行任务。

在 Swoole 中,Parallel 不是“并发映射”,而是真正基于多进程的并行执行机制;它和串行执行在底层模型、资源占用、错误隔离、返回顺序上完全不同,不能混用或误当作协程并发来理解。
Parallel 是多进程并行,不是协程并发
Swoole 的 Parallel 类(自 v4.5.0 起)启动的是独立子进程,每个任务运行在隔离的 PHP 进程中,共享内存仅限于初始化时传递的只读数据。这和 Swoole\Coroutine 下的协程并发(单进程内多任务交替调度)有本质区别:
- 协程并发:所有任务共享同一进程内存、全局变量、扩展状态;一个协程崩溃可能影响整个进程
-
Parallel并行:每个子进程完全独立,崩溃互不影响;但进程创建/销毁开销大,不适合高频短任务 - 典型误用:
Parallel里调用go或Co::sleep会报错——子进程不支持协程调度器
Parallel::wait() 阻塞等待所有完成,但返回无序
Parallel::wait() 会阻塞当前进程直到所有子进程退出,但它不保证返回结果的顺序与添加任务的顺序一致。这是因为子进程执行时间不可控,且进程间无同步协调:
- 你用
$p->add(function () { return 'A'; }); $p->add(function () { return 'B'; });,结果可能是['B', 'A'] - 若需顺序对应,必须显式绑定键名:
$p->add('task1', function () { ... });,再用$p->get('task1')取值 - 不要依赖
array_values($p->wait())的索引顺序——这不是 bug,是并行模型的自然表现
串行执行用普通循环,别硬套 Parallel
如果任务之间有强依赖(如后一个要读前一个的返回值),或数据需链式传递,Parallel 完全不适用。此时应直接用 foreach + 普通函数调用:
- 串行场景示例:解析 JSON → 校验字段 → 写入数据库 → 发送通知
- 强行塞进
Parallel会导致数据不可达、逻辑断裂,甚至因进程隔离引发Undefined variable或Call to undefined function - 性能对比:串行 4 个 100ms 任务 = 400ms;
Parallel启停开销可能让总耗时反超 500ms,尤其在低负载机器上
错误处理必须在子进程内捕获
Parallel 子进程中的异常不会自动冒泡到主进程,未捕获的 Fatal error 会导致子进程静默退出,主进程 wait() 返回 null 或空结果:
- 务必在每个
add()的闭包里加try/catch,并显式返回错误信息 - 例如:
$p->add(function () { try { risky_op(); } catch (\Throwable $e) { return ['error' => $e->getMessage()]; } }); - 主进程不能依赖
set_exception_handler捕获子进程异常——那玩意儿在另一个进程地址空间里根本没注册
真正容易被忽略的一点:Parallel 启动的子进程会继承父进程的扩展状态(比如已加载的 PDO 连接、Redis 实例),但这些资源在子进程中不可用——它们是进程私有的。任何试图复用父进程连接的操作,都会触发“Connection closed”或 segmentation fault。必须在每个子进程闭包内部重新初始化所需客户端。这不是配置问题,是 POSIX 进程模型的铁律。











