在 PHP 中使用异步进程。
对于大多数用 PHP 编写的程序来说,其唯一目的是执行一个由多个任务组成的简单进程,这些任务必须按顺序执行,例如数据处理。我们总是不得不忍受同步编程的停止和等待。同步代码执行被称为阻塞,这意味着任务将逐个执行。那么,如果我们想在运行任务时避免它们相互阻塞,那该怎么办呢?这意味着我们需要一个非阻塞进程。这种方法需要在 PHP 中应用异步编程方法,此时任务将彼此不依赖地执行。
在 PHP 中实现非阻塞执行的常用方法是实现队列处理。任务会被持久化到传输系统(例如 MySQL、Redis、Amazon SQS 等),由后台工作进程检索并执行,从而不会阻塞创建任务的主进程。Laravel 应用程序提供了一种队列机制,允许将任务(此处称为作业)延迟到以后再处理。
另一种方法是并行运行所有已定义的任务。这种方法的好处是,当特定任务完成后,它可以立即将控制权交还给主进程,并承诺执行代码,并在稍后通知我们结果(例如回调)。并行处理方法的用例可能很少;例如,执行图像处理并向某个外部服务发出获取请求。
让我们通过一个非常简单的用例来看一下 PHP 中同步和异步(并行)进程之间的区别。
同步代码
foreach (range(1, 5) as $i) {
$output = $i * 2;
echo $output . "\n";
}
异步代码
use Spatie\Async\Pool;
$pool = Pool::create();
foreach (range(1, 5) as $i) {
$pool[] = async(function () use ($i) {
$output = $i * 2;
return $output;
})->then(function (int $output) {
echo $output . "\n";
});
}
await($pool);
当我们执行第一个代码时,我们将按以下顺序获得输出值:
2
4
6
8
10
重试执行,我们将得到与上面相同的输出序列……因此,每个乘法运算都会等待执行,然后再执行下一个。接下来,运行第二个代码块,看看会得到什么。
6
10
2
8
4
第二次重试执行:
2
6
4
10
8
一个进程恰好产生了两种不同的结果。这正是我们利用异步方法所获得的……我们的小任务可以以互不阻塞的方式执行。每个乘法任务独立执行,有些任务执行得比其他任务快,因此输出结果比较无序。另外,请注意我们的 async 函数附加了一个 then 方法,该方法负责收回控制权,并接受一个回调函数作为参数,该回调函数现在可以对接收到的输出执行额外的操作。
Spatie 的朋友们制作了这个很棒的spatie/async
包,它有助于并行执行任务。您可以通过 composer 安装它:
composer require spatie/async
该包提供了一种简洁的方式来与创建的并行执行任务进行交互。任务的事件监听器如下所述:
- 通过其方法,可以在任务完成时通过回调执行另一个操作
then
。 - 当特定任务使用该方法抛出异常时,错误处理更容易控制
catch
。 - 比如说,一个任务没有完成其操作,一种
timeout
方法可以处理这种情况。
事件监听器与任务挂钩,如下所示:
$pool
->add(function () {
// Task to be performed in a Parallel process
})
->then(function ($output) {
// On success, `$output` is returned by the process or callable you passed to the queue.
})
->catch(function ($exception) {
// When an exception is thrown from within a process, it's caught and passed here.
})
->timeout(function () {
// Ohh No! A process took too long to finish. Let's do something
})
;
要了解有关该软件包的更多信息,请阅读其中spatie/async
一位贡献者撰写的这篇文章,您也可以参考GitHub repo。