Concurrencia y multiproceso en PHP: enfoques modernos
PHP se asocia tradicionalmente con la ejecución síncrona de código. Sin embargo, las aplicaciones web modernas requieren procesar varias tareas simultáneamente: desde la carga de archivos hasta el análisis de grandes datos. En este artículo analizaremos cómo implementar concurrencia y multiproceso en PHP, utilizando herramientas integradas y de terceros.
¿Qué es concurrencia y multiproceso?
Concurrencia es la capacidad de un programa para ejecutar varias tareas "simultáneamente", alternando entre ellas. Multiproceso es un caso particular de concurrencia, donde las tareas se ejecutan en hilos separados del sistema operativo.
En PHP, hasta la versión 7.4 no existía soporte nativo para hilos. Sin embargo, con la aparición de las extensiones pthreads y parallel, así como la mejora en el trabajo con procesos, la situación cambió.
1. Multiproceso con la extensión parallel
La extensión parallel es una forma moderna de trabajar con hilos en PHP 7.4+. Utiliza memoria compartida, lo que permite intercambiar datos eficientemente entre hilos.
Instalación:
pecl install parallelEjemplo de creación de un hilo:
<?phpuse parallel\\Runtime;
$runtime = new Runtime();$future = $runtime->run(function() { // Cálculos pesados $result = 0; for ($i = 0; $i < 1000000; $i++) { $result += sqrt($i); } return $result;});
echo "El hilo principal continúa trabajando...";$result = $future->value(); // Obtenemos el resultadoecho "Resultado: $result";?>Ventajas de parallel:
- Soporte nativo de hilos sin dependencias adicionales
- Memoria compartida entre hilos
- Posibilidad de pasar closures y objetos
Desventajas:
- Requiere PHP 7.4+ y ZTS (Zend Thread Safety)
- No compatible con Windows
- Dificultades con la depuración
2. Asincronía mediante procesos: pcntl_fork
Si no necesita memoria compartida, puede usar procesos con pcntl_fork(). Este es el enfoque clásico de Unix.
<?php$pid = pcntl_fork();
if ($pid == -1) { die("Error al crear el proceso");} elseif ($pid) { // Proceso padre echo "Padre: esperando al proceso hijo..."; pcntl_wait($status); echo "Padre: proceso hijo finalizado";} else { // Proceso hijo echo "Hijo: ejecutando tarea..."; sleep(3); exit(0);}?>Características del trabajo con procesos:
- Cada proceso tiene su propia memoria
- Para intercambiar datos use sockets, memoria compartida (shmop) o archivos
- Es necesario manejar procesos zombie mediante
pcntl_wait()
3. Concurrencia mediante funciones asíncronas y ReactPHP
Para tareas de entrada/salida (solicitudes de red, lectura de archivos) es más eficiente usar bibliotecas asíncronas como ReactPHP o Amp. No crean hilos, pero permiten ejecutar tareas concurrentemente mediante un event loop.
Ejemplo con 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);}
// Esperamos todas las solicitudes en paralelo$results = [];foreach ($promises as $promise) { $results[] = $promise->wait();}
$loop->run();print_r($results);?>