3.4 KiB
Concurrency
- Introduction
- Running Concurrent Tasks
- Deferring Concurrent Tasks
Introduction
Sometimes you may need to execute several slow tasks which do not depend on
one another. In many cases, significant performance improvements can be
realized by executing the tasks concurrently. Laravel's Concurrency facade
provides a simple, convenient API for executing closures concurrently.
How it Works
Laravel achieves concurrency by serializing the given closures and dispatching them to a hidden Artisan CLI command, which unserializes the closures and invokes it within its own PHP process. After the closure has been invoked, the resulting value is serialized back to the parent process.
The Concurrency facade supports three drivers: process (the default),
fork, and sync.
The fork driver offers improved performance compared to the default
process driver, but it may only be used within PHP's CLI context, as PHP
does not support forking during web requests. Before using the fork driver,
you need to install the spatie/fork package:
1composer require spatie/fork
composer require spatie/fork
The sync driver is primarily useful during testing when you want to disable
all concurrency and simply execute the given closures in sequence within the
parent process.
Running Concurrent Tasks
To run concurrent tasks, you may invoke the Concurrency facade's run
method. The run method accepts an array of closures which should be executed
simultaneously in child PHP processes:
1use Illuminate\Support\Facades\Concurrency;
2use Illuminate\Support\Facades\DB;
3
4[$userCount, $orderCount] = Concurrency::run([
5 fn () => DB::table('users')->count(),
6 fn () => DB::table('orders')->count(),
7]);
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;
[$userCount, $orderCount] = Concurrency::run([
fn () => DB::table('users')->count(),
fn () => DB::table('orders')->count(),
]);
To use a specific driver, you may use the driver method:
1$results = Concurrency::driver('fork')->run(...);
$results = Concurrency::driver('fork')->run(...);
Or, to change the default concurrency driver, you should publish the
concurrency configuration file via the config:publish Artisan command and
update the default option within the file:
1php artisan config:publish concurrency
php artisan config:publish concurrency
Deferring Concurrent Tasks
If you would like to execute an array of closures concurrently, but are not
interested in the results returned by those closures, you should consider
using the defer method. When the defer method is invoked, the given
closures are not executed immediately. Instead, Laravel will execute the
closures concurrently after the HTTP response has been sent to the user:
1use App\Services\Metrics;
2use Illuminate\Support\Facades\Concurrency;
3
4Concurrency::defer([
5 fn () => Metrics::report('users'),
6 fn () => Metrics::report('orders'),
7]);
use App\Services\Metrics;
use Illuminate\Support\Facades\Concurrency;
Concurrency::defer([
fn () => Metrics::report('users'),
fn () => Metrics::report('orders'),
]);