spatie/fork

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

资助包维护!
spatie

1.2.2 2023-12-07 16:42 UTC

This package is auto-updated.

Last update: 2024-09-07 18:11:31 UTC


README

Latest Version on Packagist Tests GitHub Code Style Action Status Total Downloads

此包使得在PHP中并发运行代码变得容易。幕后,通过将主PHP进程分解为一个或多个子任务来实现并发。

在这个例子中,我们将调用一个假设的慢速API,所有三个闭包将同时运行。

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]; // fetched data of user 1
$results[1]; // fetched data of user 2
$results[2]; // fetched data of user 3

内部工作原理

✨ 在这个 YouTube视频 中,我们解释了该包是如何在内部工作的。

支持我们

我们投入了大量资源来创建 一流的开放式源代码包。您可以通过 购买我们的付费产品之一 来支持我们。

我们非常感谢您从家乡寄给我们明信片,并说明您正在使用我们的哪些包。您可以在 我们的联系页面 上找到我们的地址。我们将所有收到的明信片发布在 我们的虚拟明信片墙上

要求

此包需要PHP 8和默认安装在许多Unix和Mac系统中的 pcntl 扩展。

❗️ pcntl 只在CLI进程中工作,不在Web环境中工作。❗️ Alpine Linux需要 posix 来正确处理进程终止。

安装

您可以通过composer安装此包

composer require spatie/fork

用法

您可以将任意数量的闭包传递给 run。它们将并发运行。run 函数将返回一个包含执行闭包返回值的数组。

use Spatie\Fork\Fork;

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

            return 'result from task 1';
        },
        function ()  {
            sleep(1);

            return 'result from task 2';
        },
        function ()  {
            sleep(1);

            return 'result from task 3';
        },
    );

// this code will be reached this point after 1 second
$results[0]; // contains 'result from task 1'
$results[1]; // contains 'result from task 2'
$results[2]; // contains 'result from task 3'

在每个闭包前后运行代码

如果您需要在每个传递给 run 的可调用项前后执行一些代码,您可以将一个可调用项传递给 beforeafter 方法。这个传递的可调用项将在子进程中执行,在传递给 run 的可调用项执行之前或之后。

在子任务中使用 beforeafter

在这个例子中,我们将使用Laravel Eloquent模型从数据库中获取值。为了让子任务使用DB,需要重新连接到DB。传递给 before 的闭包将在为传递给 run 的闭包创建的所有子任务中运行。

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(),
    );

如果您需要在子任务的可调用项运行后执行一些清理工作,您可以在 Spatie\Fork\Fork 实例上使用 after 方法。

在父任务中使用 beforeafter

如果您需要让传递给 beforeafter 的可调用项在父任务中运行,则需要将此可调用项传递给 parent 参数。

use App\Models\User;
use Illuminate\Support\Facades\DB;
use Spatie\Fork\Fork;

 Fork::new()
    ->before(
        parent: fn() => echo 'this runs in the parent task'
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

您还可以传递不同的闭包,以便在子任务和父任务中运行

use Spatie\Fork\Fork;

Fork::new()
    ->before(
        child: fn() => echo 'this runs in the child task',
        parent: fn() => echo 'this runs in the parent task',
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

返回数据

所有输出数据都收集到一个数组中,并在所有子任务完成后立即可用。在这个例子中,$results 将包含三个条目

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

输出也适用于 after 回调,每当子任务完成后都会调用,但不是在完全结束时

$results = Fork::new()
    ->after(
        child: fn (int $i) => echo $i, // 1, 2 and 3
        parent: fn (int $i) => echo $i, // 1, 2 and 3
    )
    ->run(
        fn () => 1,
        fn () => 2,
        fn () => 3,
    );

最后,子任务的返回值使用 PHP 内置的 serialize 方法进行序列化。这意味着你可以返回任何 PHP 中可以序列化的东西,包括对象

$result = Fork::new()
    ->run(
        fn () => new DateTime('2021-01-01'),
        fn () => new DateTime('2021-01-02'),
    );

配置并发

默认情况下,所有可调用项都将并行运行。但是,你可以配置最大并发进程数

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

在这种情况下,前两个函数将立即运行,一旦其中之一完成,最后一个也将开始运行。

测试

composer test

变更日志

请参阅 变更日志 了解最近有哪些更改。

贡献

请参阅 贡献指南 了解详细信息。

安全漏洞

请查阅 我们的安全策略 了解如何报告安全漏洞。

鸣谢

许可协议

MIT 许可协议(MIT)。请参阅 许可文件 了解更多信息。