Concurrencia y subprocesamiento múltiple en PHP: nivel avanzado
PHP tradicionalmente se asocia con la ejecución síncrona de scripts, pero las aplicaciones web modernas requieren procesar múltiples tareas simultáneamente. La concurrencia (concurrency) y el subprocesamiento múltiple (multithreading) permiten aumentar significativamente el rendimiento, acelerar el procesamiento de solicitudes y utilizar eficientemente los recursos del servidor. En este artículo, exploraremos técnicas avanzadas para trabajar con subprocesamiento múltiple en PHP, incluyendo la extensión pthreads, procesos paralelos, llamadas asíncronas y gestión de subprocesos.
1. Fundamentos del subprocesamiento múltiple en PHP: extensión pthreads
La extensión pthreads es la herramienta principal para trabajar con subprocesos en PHP. Permite crear, ejecutar y sincronizar múltiples subprocesos de ejecución dentro de un mismo proceso. Importante: pthreads solo funciona en modo CLI y requiere compilar PHP con soporte para subprocesos (ZTS — Zend Thread Safety).
Ejemplo de creación de un subproceso simple:
<?phpclass WorkerThread extends Thread { private $id; public function __construct($id) { $this->id = $id; } public function run() { echo "Subproceso {$this->id} iniciado"; sleep(2); echo "Subproceso {$this->id} finalizado"; }}
$threads = [];for ($i = 1; $i <= 5; $i++) { $threads[$i] = new WorkerThread($i); $threads[$i]->start();}
foreach ($threads as $thread) { $thread->join();}?>El método start() inicia el subproceso, y join() espera a que finalice. Sin join(), el subproceso principal podría finalizar antes que los subprocesos hijos.
2. Sincronización de subprocesos y recursos compartidos
Al trabajar con subprocesamiento múltiple, es fundamental sincronizar el acceso a datos compartidos. PHP proporciona varios mecanismos: objetos Mutex, Volatile y Threaded.
Mutex (exclusión mutua) — bloquea el acceso a un recurso para otros subprocesos mientras uno trabaja con él.
<?phpclass Counter extends Threaded { private $count = 0; public function increment() { $this->synchronized(function() { $this->count++; }); } public function getCount() { return $this->count; }}
class IncrementThread extends Thread { private $counter; public function __construct(Counter $counter) { $this->counter = $counter; } public function run() { for ($i = 0; $i < 1000; $i++) { $this->counter->increment(); } }}
$counter = new Counter();$threads = [];
for ($i = 0; $i < 10; $i++) { $threads[$i] = new IncrementThread($counter); $threads[$i]->start();}
foreach ($threads as $thread) { $thread->join();}
echo "Valor final: " . $counter->getCount(); // 10000?>Volatile — garantiza que los cambios en una variable sean visibles para todos los subprocesos. Use Volatile para tipos de datos simples.
$sharedFlag = new Volatile();$sharedFlag->flag = false;
// En un subproceso$sharedFlag->flag = true;
// En otro subprocesoif ($sharedFlag->flag) { echo "Bandera establecida";}3. Procesos paralelos y tareas asíncronas
Si la extensión pthreads no está disponible (por ejemplo, en un hosting compartido), se pueden usar procesos paralelos mediante pcntl_fork() o bibliotecas asíncronas como ReactPHP o Amp.
pcntl_fork() — crea un proceso hijo que se ejecuta en paralelo con el proceso padre.
<?php$pid = pcntl_fork();if ($pid == -1) { die("No se pudo crear el proceso");} elseif ($pid)