Concurrence et multithreading en PHP : approches modernes
PHP est traditionnellement associé à l'exécution synchrone de code. Cependant, les applications web modernes nécessitent le traitement de plusieurs tâches simultanément : du téléchargement de fichiers à l'analyse de grandes données. Dans cet article, nous allons voir comment implémenter la concurrence et le multithreading en PHP, en utilisant des outils intégrés et tiers.
Qu'est-ce que la concurrence et le multithreading ?
La concurrence est la capacité d'un programme à exécuter plusieurs tâches « simultanément » en basculant entre elles. Le multithreading est un cas particulier de concurrence où les tâches sont exécutées dans des threads séparés du système d'exploitation.
Dans PHP avant la version 7.4, il n'y avait pas de support natif des threads. Cependant, avec l'apparition des extensions pthreads et parallel, ainsi que l'amélioration du travail avec les processus, la situation a changé.
1. Multithreading avec l'extension parallel
L'extension parallel est une méthode moderne pour travailler avec les threads en PHP 7.4+. Elle utilise la mémoire partagée, ce qui permet d'échanger efficacement des données entre les threads.
Installation :
pecl install parallelExemple de création d'un thread :
<?phpuse parallel\\Runtime;
$runtime = new Runtime();$future = $runtime->run(function() { // Calculs lourds $result = 0; for ($i = 0; $i < 1000000; $i++) { $result += sqrt($i); } return $result;});
echo "Le thread principal continue son travail...";$result = $future->value(); // Récupération du résultatecho "Résultat : $result";?>Avantages de parallel :
- Support natif des threads sans dépendances superflues
- Mémoire partagée entre les threads
- Possibilité de transmettre des closures et des objets
Inconvénients :
- Nécessite PHP 7.4+ et ZTS (Zend Thread Safety)
- Non supporté sur Windows
- Difficultés de débogage
2. Asynchronisme via les processus : pcntl_fork
Si vous n'avez pas besoin de mémoire partagée, vous pouvez utiliser les processus avec pcntl_fork(). C'est une approche Unix classique.
<?php$pid = pcntl_fork();
if ($pid == -1) { die("Erreur de création du processus");} elseif ($pid) { // Processus parent echo "Parent : attente du processus enfant..."; pcntl_wait($status); echo "Parent : processus enfant terminé";} else { // Processus enfant echo "Enfant : exécution de la tâche..."; sleep(3); exit(0);}?>Caractéristiques du travail avec les processus :
- Chaque processus a sa propre mémoire
- Pour échanger des données, utilisez les sockets, la mémoire partagée (shmop) ou les fichiers
- Il faut gérer les processus zombies avec
pcntl_wait()
3. Concurrence via les fonctions asynchrones et ReactPHP
Pour les tâches d'entrée-sortie (requêtes réseau, lecture de fichiers), il est plus efficace d'utiliser des bibliothèques asynchrones comme ReactPHP ou Amp. Elles ne créent pas de threads, mais permettent d'exécuter des tâches de manière concurrente via une event loop.
Exemple avec 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);}
// Attente de toutes les requêtes en parallèle$results = [];foreach ($promises as $promise) { $results[] = $promise->wait();}
$loop->run();print_r($results);?>