首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用于并发运行 PHP 代码的轻量级解决方案

用于并发运行 PHP 代码的轻量级解决方案

作者头像
Tinywan
发布2026-07-01 16:22:00
发布2026-07-01 16:22:00
410
举报
文章被收录于专栏:开源技术小栈开源技术小栈

概述

这个包可以轻松地在 PHP 中实现并发运行代码。在底层,并发是通过将主 PHP 进程分叉(fork)到一个或多个子任务来实现的。

示例

在这个例子中,我们将调用一个假想的慢速 API,三个闭包会同时运行:

代码语言:javascript
复制
use Spatie\Fork\Fork;

$results = Fork::new()
    ->run(
        fn () => (new Api)->fetchData(userId: 1),
        fn () => (new Api)->fetchData(userId: 2),
        fn () => (new Api)->fetchData(userId: 3),
    );

$results[0]; // 用户 1 的获取数据
$results[1]; // 用户 2 的获取数据
$results[2]; // 用户 3 的获取数据

要求

  • PHP 8+
  • 需要安装 pcntl 扩展(大多数 Unix 和 macOS 系统默认已安装)
  • 仅支持 CLI 环境,不支持 Web 请求上下文
  • Alpine Linux 需要 posix 扩展才能正确处理进程终止

安装

通过 Composer 安装:

代码语言:javascript
复制
composer require spatie/fork

使用方法

您可以向 run 方法传递任意数量的闭包,它们将并发执行。run 函数会返回一个包含所有执行结果的数组。

代码语言:javascript
复制
use Spatie\Fork\Fork;

$results = Fork::new()
    ->run(
        function ()  {
            sleep(1);

            return'来自任务 1 的结果';
        },
        function ()  {
            sleep(1);

            return'来自任务 2 的结果';
        },
        function ()  {
            sleep(1);

            return'来自任务 3 的结果';
        },
    );

// 此代码会在 1 秒后执行
$results[0]; // 包含 '来自任务 1 的结果'
$results[1]; // 包含 '来自任务 2 的结果'
$results[2]; // 包含 '来自任务 3 的结果'

在每个闭包前后运行代码

如果您需要在传递给 run 的每个可调用函数之前或之后执行一些代码,可以使用 beforeafter 方法。这些可调用函数会在子进程中、对应闭包执行前或执行后运行。

在子任务中使用 before 和 after

以下示例中,我们使用 Laravel 的 Eloquent 模型从数据库获取值。为了让子任务能使用数据库,必须重新连接数据库。传递给 before 的闭包会在为 run 创建的每个子任务中运行。

代码语言:javascript
复制
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Spatie\Fork\Fork;

Fork::new()
    ->before(fn () => DB::connection('mysql')->reconnect())
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

如果您需要在子任务中执行完可调用函数后进行清理,可以使用 after 方法。

在父任务中使用 before 和 after

如果您希望 beforeafter 中的可调用函数在父任务中运行,请将其传递给 parent 参数。

代码语言:javascript
复制
use App\Models\User;
useIlluminate\Support\Facades\DB;
useSpatie\Fork\Fork;

Fork::new()
    ->before(
        parent: fn() => echo'这在父任务中运行'
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

您也可以分别传递在子任务和父任务中运行的不同闭包:

代码语言:javascript
复制
use Spatie\Fork\Fork;

Fork::new()
    ->before(
        child: fn() => echo '这在子任务中运行',
        parent: fn() => echo '这在父任务中运行',
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

返回数据

所有输出数据都会收集到一个数组中,并在所有子任务完成后立即可用。在下面的例子中,$results 将包含三个项:

代码语言:javascript
复制
$results = Fork::new()
    ->run(
        fn () => (new Api)->fetchData(userId: 1),
        fn () => (new Api)->fetchData(userId: 2),
        fn () => (new Api)->fetchData(userId: 3),
    );

输出数据也可以在 after 回调中使用(每个子任务完成时就会调用,而非全部结束后):

代码语言:javascript
复制
$results = Fork::new()
    ->after(
        child: fn (int $i) => echo $i, // 依次输出 1, 2, 3
        parent: fn (int $i) => echo $i, // 依次输出 1, 2, 3
    )
    ->run(
        fn () => 1,
        fn () => 2,
        fn () => 3,
    );

子任务的返回值使用 PHP 内置的 serialize 方法进行序列化。这意味着您可以返回任何 PHP 中正常可序列化的内容,包括对象:

代码语言:javascript
复制
$result = Fork::new()
    ->run(
        fn () => new DateTime('2021-01-01'),
        fn () => new DateTime('2021-01-02'),
    );

配置并发数量

默认情况下,所有可调用函数都会并行运行。您也可以设置最大并发进程数:

代码语言:javascript
复制
$results = Fork::new()
    ->concurrent(2)   // 最多同时运行 2 个进程
    ->run(
        fn () => 1,
        fn () => 2,
        fn () => 3,
    );

在这种情况下,前两个函数会立即运行,当其中一个完成后,第三个函数才会开始执行。

这个包特别适合 Laravel Artisan 命令、CLI 脚本中需要并行处理耗时任务的场景(如批量 API 调用、数据处理等)。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 示例
  • 要求
  • 安装
  • 使用方法
  • 在每个闭包前后运行代码
    • 在子任务中使用 before 和 after
    • 在父任务中使用 before 和 after
  • 返回数据
  • 配置并发数量
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档