spatie/async:PHP PCNTL 并行任务池的使用与底层机制
- 项目仓库:
- 安装:
composer require spatie/async - 依赖:需要 PHP 的
pcntl与posix扩展;缺少时会自动退化为同步执行
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 运行时没有安装 pcntl 和 posix 时,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。