Concurrency and Multithreading in PHP: Modern Approaches
PHP is traditionally associated with synchronous code execution. However, modern web applications require handling multiple tasks simultaneously: from file uploads to parsing large data. In this article, we will explore how to implement concurrency and multithreading in PHP using built-in and third-party tools.
What are Concurrency and Multithreading?
Concurrency is the ability of a program to execute multiple tasks "simultaneously" by switching between them. Multithreading is a special case of concurrency where tasks are executed in separate operating system threads.
In PHP prior to version 7.4, there was no native support for threads. However, with the introduction of pthreads and parallel extensions, as well as improved process handling, the situation has changed.
1. Multithreading with the parallel Extension
The parallel extension is a modern way to work with threads in PHP 7.4+. It uses shared memory, allowing efficient data exchange between threads.
Installation:
pecl install parallelExample of creating a thread:
<?phpuse parallel\\Runtime;
$runtime = new Runtime();$future = $runtime->run(function() { // Heavy computations $result = 0; for ($i = 0; $i < 1000000; $i++) { $result += sqrt($i); } return $result;});
echo "Main thread continues working...";$result = $future->value(); // Get the resultecho "Result: $result";?>Advantages of parallel:
- Native thread support without extra dependencies
- Shared memory between threads
- Ability to pass closures and objects
Disadvantages:
- Requires PHP 7.4+ and ZTS (Zend Thread Safety)
- Not supported on Windows
- Debugging difficulties
2. Asynchrony through Processes: pcntl_fork
If you don't need shared memory, you can use processes with pcntl_fork(). This is a classic Unix approach.
<?php$pid = pcntl_fork();
if ($pid == -1) { die("Error creating process");} elseif ($pid) { // Parent process echo "Parent: waiting for child process..."; pcntl_wait($status); echo "Parent: child process completed";} else { // Child process echo "Child: executing task..."; sleep(3); exit(0);}?>Features of working with processes:
- Each process has its own memory
- Use sockets, shared memory (shmop), or files for data exchange
- Zombie processes must be handled via
pcntl_wait()
3. Concurrency through Asynchronous Functions and ReactPHP
For I/O tasks (network requests, file reading), it is more efficient to use asynchronous libraries such as ReactPHP or Amp. They do not create threads but allow concurrent task execution through an event loop.
Example with ReactPHP:
<?phprequire 'vendor/autoload.php';
use React\\EventLoop\\Factory;use React\\Http\\Browser;
$loop = Factory::create();$client = new Browser($loop);
$promises = [];$urls = [ 'https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3'];
foreach ($urls as $url) { $promises[] = $client->get($url);}
// Wait for all requests in parallel$results = [];foreach ($promises as $promise) { $results[] = $promise->wait();}
$loop->run();print_r($results);?>