编程 spatie/async:PHP PCNTL 并行任务池的使用与底层机制

2026-09-08 21:33:56

spatie/async:PHP PCNTL 并行任务池的使用与底层机制

  • 项目仓库:
  • 安装:composer require spatie/async
  • 依赖:需要 PHP 的 pcntlposix 扩展;缺少时会自动退化为同步执行

spatie/async 是对 PHP PCNTL 扩展的小封装,把任务放到独立进程中并行运行。它不是事件循环模型,而是直接创建 PHP 子进程;适合批量执行互相独立的阻塞任务。

基本用法

use Spatie\Async\Pool;

$pool = Pool::create();

foreach ($things as $thing) {
$pool->add(function () use ($thing) {
// Do a thing
})->then(function ($output) {
// Handle success
})->catch(function (Throwable $exception) {
// Handle exception
});
}

$pool->wait();

add() 返回一个 ParallelProcess,可以链式注册回调:

  • then():进程成功;$output 是闭包返回值
  • catch():子进程内部抛出的异常会被捕获到这里
  • timeout():进程超时回调

函数式 API

进程池也提供 async() / await() 辅助函数:

$pool[] = async(function () {
// ...
})->then(function ($output) {
// ...
});

await($pool);

错误处理

子进程抛出的 Exception / Error 可以由单个进程的 catch 回调处理。如果没有挂错误处理器,异常会在 await() / $pool->wait() 时从父进程抛出来。

如果子进程意外终止且没有抛出 Throwable,写入 stderr 的内容会被包装成 Spatie\Async\ParallelError 并抛到父进程。

catch 回调支持类型提示,多个错误处理器可以分别针对不同异常类型;异常一旦被某个处理器处理,就不会再触发其他处理器。

停止进程池

$pool->stop();

调用 stop() 会提前停止进程池,阻止后续再启动新进程。被停止的进程池不能再复用,需要新建实例。原文档给出的例子是:生成 10000 个进程产生随机数,当某个结果返回 100 时调用 stop()

也可以指定子进程使用的 PHP 二进制:

Pool::create()->withBinary('/path/to/php');

Pool 配置

$pool = Pool::create()
->concurrency(20)
->timeout(15)
->autoload(__DIR__.'/../../vendor/autoload.php')
->sleepTime(50000);

含义如下:

  • concurrency():同时运行的最大进程数
  • timeout():单个进程最长完成时间,单位秒
  • autoload():子进程使用的 autoloader 路径
  • sleepTime():循环重新检查进程状态前休眠的微秒数

Task 类

任务需要更多初始化工作时,可以继承 Task

use Spatie\Async\Task;

class MyTask extends Task
{
public function configure()
{
// setup,例如初始化依赖容器
}

public function run()
{
// 真正要做的工作
}
}

$pool->add(new MyTask());

简单的任务也可以直接传入 invokable 对象:

$pool->add(new InvokableClass());

同步 fallback

当当前 PHP 运行时没有安装 pcntlposix 时,Pool 会自动退回同步模式,任务按顺序执行。可以用静态方法判断平台是否支持异步:

Pool::isSupported();

同步模式下,Task 只会调用其 run() 方法。

底层机制

进程管理基于 symfony/process。库会动态创建子进程、在子进程中执行 PHP 脚本来实现并行。需要注意不要一次性生成过量进程导致应用崩溃,这部分由 Pool 的调度逻辑来约束。

await() / $pool->wait() 内部是一个 while 循环,等待所有子进程结束。判断进程是否结束用的是 SIGCHLD 信号监听——PHP 7.1 之后对异步信号有更好的支持,这也比 process forks 或 socket 通信更高效。相关 RFC:

子进程结束时触发 success 事件并交给 then() 回调;失败或超时则更新状态后继续循环,直到所有进程处理完,父进程再继续向下执行。

与 ReactPHP / Amp 的路线差异

spatie/async 与 ReactPHP、Amp 属于不同思路。作者专门写过选型对比:

开源协议与维护

MIT License,由 Spatie 维护,作者 Brent Roose。

复制全文 生成海报 PHP 并发 PCNTL 开源库 进程

推荐文章

程序员茄子在线接单