Implementación simple de subprocesos múltiples de PHP

El multiproceso en PHP está ausente "fuera de la caja", por lo que se inventaron muchas opciones para su implementación, incluidas las extensiones pthreads , AzaThread (CThread) e incluso algunos de los desarrollos propios de los desarrolladores de PHP.

La principal desventaja para mí fue que había demasiadas "campanas y silbatos" para estas soluciones: no siempre es necesario el intercambio de información entre los hilos y el proceso principal o para ahorrar recursos. Siempre debe existir la capacidad de resolver el problema de manera rápida y rentable.

Quiero hacer una reserva con anticipación para que los grandes secretos no se abran en esta publicación; es más probable para los principiantes en el idioma, y ​​decidí publicarlo solo porque me había encontrado con un problema y, al no haber encontrado una solución preparada, hice una especie de emulación multiproceso.

Entonces, la tarea es procesar una gran cantidad de datos que ingresaron a nuestro script. Mi tarea consistía en procesar una matriz JSON de información textual, digiriendo que la secuencia de comandos tenía que recopilar una confirmación igualmente grande para PostgreSQL.

En primer lugar, recopilamos los datos en el archivo principal:

index.php

// bigdata.json -    .      - ,     .. $big_json = file_get_contents('bigdata.json'); $items = json_decode($big_json, true); //   php    ,    , ,  unset($big_json); // ... 

El tamaño de la matriz fluctuó alrededor de 400 mb (luego se redujo a ~ 50 mb), y toda la información fue textual. No es difícil estimar la velocidad con la que todo fue digerido, y dado que el script se ejecutó en cron cada 15 minutos, y la potencia de cálculo era regular, el rendimiento sufrió mucho.

Después de recibir los datos, puede estimar su volumen y, si es necesario, calcular el número necesario de subprocesos para cada núcleo de CPU, o simplemente puede decidir que habrá 4 subprocesos y calcular el número de filas para cada subproceso:

index.php

  // ... $threads = 4; $strs_per_thread = ceil(count($items) / $threads); //      -   echo "Items: ".count($items)."\n"; echo "Items per thread: ".$strs_per_thread."\n"; echo "Threads: ".$threads."\n"; // ... 

Vale la pena mencionarlo de inmediato: tal cálculo "de frente" no dará un resultado exacto por el número de elementos para cada flujo. Es más probable que se simplifiquen los cálculos.

Y ahora la esencia misma: creamos tareas para cada hilo y lo ejecutamos. Haremos esta "frente" - creando una tarea para el segundo archivo - thread.php. Actuará como un "flujo", recibiendo una variedad de elementos para procesar y comenzar independientemente del script principal:

index.php

  // ... for($i = 0; $i < $threads; $i++){ if($i == 0) { passthru("(php -f thread.php 0 ".$strs_per_thread." & ) >> /dev/null 2>&1"); } if($i == $threads-1) { passthru("(php -f thread.php ".($strs_per_thread * $i)." ".count($items)." & ) >> /dev/null 2>&1"); } if(($i !== 0)&&($i !== $threads-1)) { $start = $strs_per_thread * $i + 1; $end = $start -1 + $strs_per_thread; passthru("(php -f thread.php ".$start." ".$end." & ) >> /dev/null 2>&1"); } } // ... 

La función passthru () se usa para ejecutar comandos de consola, pero el script esperará a que se complete cada uno de ellos. Para hacer esto, envolvemos el comando de inicio en un conjunto de declaraciones que iniciarán el proceso y no devolverán nada de inmediato, iniciando el proceso y el proceso principal no dejará de esperar a que cada hijo se ejecute:

 #  ,    ,   Linux- (php -f thread.php start stop & ) >> /dev/null 2>&1 

Lo que está sucediendo exactamente aquí, desafortunadamente, no puedo decirlo con certeza: mi conocido Linuxoid me sugirió un conjunto de parámetros. Si puedes descifrar esta magia en los comentarios, te lo agradeceré y complementaré la publicación.

Archivo thread.php:

 $start = $argv[1]; $stop = $argv[2]; for ($i = $start; $i <= $stop; $i++) { // -          } 

De esta manera, bastante simple, puede implementar la emulación de subprocesos múltiples en PHP.

Si reducimos todo el ejemplo a salida en seco, entonces creo que sonaría así: el hilo principal a través de la línea de comando inicia procesos secundarios, diciéndoles qué información procesar.

Cuando digo "emulación", quiero decir que con este método de implementación no hay forma de intercambiar información entre hilos o entre hilos primarios y secundarios. Es adecuado si se sabe de antemano que tales características no son necesarias.

Source: https://habr.com/ru/post/448464/


All Articles