¿Cómo se pueden utilizar subprocesos múltiples en aplicaciones PHP?
¿Existe una forma realista de implementar un modelo multiproceso en PHP, ya sea de verdad o simplemente simulándolo? Hace algún tiempo se sugirió que se podía forzar al sistema operativo a cargar otra instancia del ejecutable PHP y manejar otros procesos simultáneos.
El problema con esto es que cuando el código PHP termina de ejecutarse, la instancia de PHP permanece en la memoria porque no hay forma de eliminarla desde PHP. Entonces, si estás simulando varios hilos, puedes imaginar lo que sucederá. Por lo tanto, todavía estoy buscando una forma de realizar o simular de manera efectiva el subproceso múltiple desde PHP. ¿Algunas ideas?
Advertencia : esta extensión se considera sin mantenimiento y muerta. Advertencia: la extensión pthreads no se puede utilizar en un entorno de servidor web. Por lo tanto, el subproceso en PHP está restringido únicamente a aplicaciones basadas en CLI. Advertencia : pthreads (v3) solo se puede usar con PHP 7.2+: esto se debe a que el modo ZTS no es seguro en 7.0 y 7.1.
https://www.php.net/manual/en/intro.pthreads.php
El subproceso múltiple es posible en php
Sí, puedes hacer subprocesos múltiples en PHP con pthreads
De la documentación de PHP :
pthreads es una API orientada a objetos que proporciona todas las herramientas necesarias para subprocesos múltiples en PHP. Las aplicaciones PHP pueden crear, leer, escribir, ejecutar y sincronizar con Threads, Workers y objetos Threaded.
Advertencia : la extensión pthreads no se puede utilizar en un entorno de servidor web. Por lo tanto, la creación de subprocesos en PHP debería limitarse a aplicaciones basadas en CLI.
Prueba sencilla
#!/usr/bin/php
<?php
class AsyncOperation extends Thread {
public function __construct($arg) {
$this->arg = $arg;
}
public function run() {
if ($this->arg) {
$sleep = mt_rand(1, 10);
printf('%s: %s -start -sleeps %d' . "\n", date("g:i:sa"), $this->arg, $sleep);
sleep($sleep);
printf('%s: %s -finish' . "\n", date("g:i:sa"), $this->arg);
}
}
}
// Create a array
$stack = array();
//Initiate Multiple Thread
foreach ( range("A", "D") as $i ) {
$stack[] = new AsyncOperation($i);
}
// Start The Threads
foreach ( $stack as $t ) {
$t->start();
}
?>
Primer intento
12:00:06pm: A -start -sleeps 5
12:00:06pm: B -start -sleeps 3
12:00:06pm: C -start -sleeps 10
12:00:06pm: D -start -sleeps 2
12:00:08pm: D -finish
12:00:09pm: B -finish
12:00:11pm: A -finish
12:00:16pm: C -finish
Segunda carrera
12:01:36pm: A -start -sleeps 6
12:01:36pm: B -start -sleeps 1
12:01:36pm: C -start -sleeps 2
12:01:36pm: D -start -sleeps 1
12:01:37pm: B -finish
12:01:37pm: D -finish
12:01:38pm: C -finish
12:01:42pm: A -finish
Ejemplo del mundo real
error_reporting(E_ALL);
class AsyncWebRequest extends Thread {
public $url;
public $data;
public function __construct($url) {
$this->url = $url;
}
public function run() {
if (($url = $this->url)) {
/*
* If a large amount of data is being requested, you might want to
* fsockopen and read using usleep in between reads
*/
$this->data = file_get_contents($url);
} else
printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
}
}
$t = microtime(true);
$g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10));
/* starting synchronization */
if ($g->start()) {
printf("Request took %f seconds to start ", microtime(true) - $t);
while ( $g->isRunning() ) {
echo ".";
usleep(100);
}
if ($g->join()) {
printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data));
} else
printf(" and %f seconds to finish, request failed\n", microtime(true) - $t);
}
Los subprocesos no están disponibles en PHP estándar, pero la programación simultánea es posible mediante el uso de solicitudes HTTP como llamadas asincrónicas.
Con la configuración de tiempo de espera de curl establecida en 1 y usando el mismo session_id para los procesos que desea asociar entre sí, puede comunicarse con las variables de sesión como en mi ejemplo a continuación. Con este método puedes incluso cerrar tu navegador y el proceso concurrente aún existirá en el servidor.
No olvide verificar el ID de sesión correcto de esta manera:
http://localhost/test/verifysession.php?sessionid=[la identificación correcta]
iniciarproceso.php
$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo $_REQUEST["PHPSESSID"];
proceso1.php
set_time_limit(0);
if ($_REQUEST["sessionid"])
session_id($_REQUEST["sessionid"]);
function checkclose()
{
global $_SESSION;
if ($_SESSION["closesession"])
{
unset($_SESSION["closesession"]);
die();
}
}
while(!$close)
{
session_start();
$_SESSION["test"] = rand();
checkclose();
session_write_close();
sleep(5);
}
verificarsesion.php
if ($_REQUEST["sessionid"])
session_id($_REQUEST["sessionid"]);
session_start();
var_dump($_SESSION);
cerrarproceso.php
if ($_REQUEST["sessionid"])
session_id($_REQUEST["sessionid"]);
session_start();
$_SESSION["closesession"] = true;
var_dump($_SESSION);
Si bien no puedes enhebrar, tienes cierto grado de control de procesos en php. Los dos conjuntos de funciones que son útiles aquí son:
Funciones de control de procesos http://www.php.net/manual/en/ref.pcntl.php
Funciones POSIX http://www.php.net/manual/en/ref.posix.php
Puede bifurcar su proceso con pcntl_fork, devolviendo el PID del niño. Luego puedes usar posix_kill para deshacerte de ese PID.
Dicho esto, si mata un proceso padre, se debe enviar una señal al proceso hijo diciéndole que muera. Si PHP no reconoce esto, puede registrar una función para administrarlo y realizar una salida limpia usando pcntl_signal.