2012-03-13 40 views
7

Tengo una función que necesita recorrer más de 20K filas de una matriz y aplicar una secuencia de comandos externa a cada una. Este es un proceso lento, ya que PHP está esperando que se ejecute el script antes de continuar con la siguiente fila.Ejecutando funciones en paralelo

Para hacer este proceso más rápido, estaba pensando en ejecutar la función en diferentes partes, al mismo tiempo. Entonces, por ejemplo, las filas 0 a 2000 como una función, 2001 a 4000 en otra, y así sucesivamente. ¿Cómo puedo hacer esto de una manera ordenada? Podría hacer diferentes trabajos cron, uno para cada función con diferentes params: myFunction(0, 2000), luego otro trabajo cron con myFunction(2001, 4000), etc. pero eso no parece muy limpio. ¿Cuál es una buena manera de hacer esto?

+1

Una vez me encontré en una situación similar. Descubrí que bifurcarme era bastante doloroso en php, así que lo hice en bash. pasó los argumentos de inicio y finalización para cada instancia del script y los ejecutó en segundo plano en el servidor. Exec es lo que estás buscando, creo. – Jake

Respuesta

6

Si desea ejecutar tareas paralelas en PHP, consideraría usar Gearman. Otro enfoque sería usar pcntl_fork(), pero preferiría los trabajadores reales cuando se basa en tareas.

+0

¿Cómo me sugeriría que haga si no tengo acceso a la configuración de php (estoy en un servidor compartido)? – Oliver

0

Eche un vistazo a pcntl_fork. Esto le permite generar procesos secundarios que luego pueden hacer el trabajo por separado que necesita.

0

No estoy seguro de si hay una solución para su situación, pero puede redirigir la salida de las llamadas al sistema a un archivo, por lo tanto, PHP no esperará hasta que el programa haya finalizado. Aunque esto puede provocar la sobrecarga de su servidor.

http://www.php.net/manual/en/function.exec.php - Si se inicia un programa con esta función, para que continúe ejecutándose en segundo plano, la salida del programa se debe redirigir a un archivo u otro flujo de salida. Si no lo hace, PHP se bloqueará hasta que finalice la ejecución del programa.

+0

Desafortunadamente el enlace está muerto. –

+0

enlace actualizado @DaveCarruthers – Michal

6

El único tiempo de espera que sufre es entre obtener los datos y procesar los datos. El procesamiento de los datos en realidad está bloqueando completamente de todos modos (simplemente tiene que esperar). Probablemente no obtendrá ningún beneficio después de aumentar el número de procesos a la cantidad de núcleos que tiene. Básicamente, creo que esto significa que la cantidad de procesos es pequeña, por lo que programar la ejecución de 2-8 procesos no suena tan horrible. Si le preocupa no poder procesar los datos mientras recupera los datos, en teoría podría obtener sus datos de la base de datos en bloques pequeños, y luego distribuir la carga de procesamiento entre algunos procesos, uno para cada núcleo.

Creo que me alineo más con el enfoque de procesos secundarios de bifurcación para ejecutar realmente los subprocesos de procesamiento. Hay una brillante demostración en los comentarios en la página doc pcntl_fork que muestra una implementación de una clase de demonio de trabajo

http://php.net/manual/en/function.pcntl-fork.php

<?php 
declare(ticks=1); 
//A very basic job daemon that you can extend to your needs. 
class JobDaemon{ 

    public $maxProcesses = 25; 
    protected $jobsStarted = 0; 
    protected $currentJobs = array(); 
    protected $signalQueue=array(); 
    protected $parentPID; 

    public function __construct(){ 
     echo "constructed \n"; 
     $this->parentPID = getmypid(); 
     pcntl_signal(SIGCHLD, array($this, "childSignalHandler")); 
    } 

    /** 
    * Run the Daemon 
    */ 
    public function run(){ 
     echo "Running \n"; 
     for($i=0; $i<10000; $i++){ 
      $jobID = rand(0,10000000000000); 

      while(count($this->currentJobs) >= $this->maxProcesses){ 
       echo "Maximum children allowed, waiting...\n"; 
       sleep(1); 
      } 

      $launched = $this->launchJob($jobID); 
     } 

     //Wait for child processes to finish before exiting here 
     while(count($this->currentJobs)){ 
      echo "Waiting for current jobs to finish... \n"; 
      sleep(1); 
     } 
    } 

    /** 
    * Launch a job from the job queue 
    */ 
    protected function launchJob($jobID){ 
     $pid = pcntl_fork(); 
     if($pid == -1){ 
      //Problem launching the job 
      error_log('Could not launch new job, exiting'); 
      return false; 
     } 
     else if ($pid){ 
      // Parent process 
      // Sometimes you can receive a signal to the childSignalHandler function before this code executes if 
      // the child script executes quickly enough! 
      // 
      $this->currentJobs[$pid] = $jobID; 

      // In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array 
      // So let's go ahead and process it now as if we'd just received the signal 
      if(isset($this->signalQueue[$pid])){ 
       echo "found $pid in the signal queue, processing it now \n"; 
       $this->childSignalHandler(SIGCHLD, $pid, $this->signalQueue[$pid]); 
       unset($this->signalQueue[$pid]); 
      } 
     } 
     else{ 
      //Forked child, do your deeds.... 
      $exitStatus = 0; //Error code if you need to or whatever 
      echo "Doing something fun in pid ".getmypid()."\n"; 
      exit($exitStatus); 
     } 
     return true; 
    } 

    public function childSignalHandler($signo, $pid=null, $status=null){ 

     //If no pid is provided, that means we're getting the signal from the system. Let's figure out 
     //which child process ended 
     if(!$pid){ 
      $pid = pcntl_waitpid(-1, $status, WNOHANG); 
     } 

     //Make sure we get all of the exited children 
     while($pid > 0){ 
      if($pid && isset($this->currentJobs[$pid])){ 
       $exitCode = pcntl_wexitstatus($status); 
       if($exitCode != 0){ 
        echo "$pid exited with status ".$exitCode."\n"; 
       } 
       unset($this->currentJobs[$pid]); 
      } 
      else if($pid){ 
       //Oh no, our job has finished before this parent process could even note that it had been launched! 
       //Let's make note of it and handle it when the parent process is ready for it 
       echo "..... Adding $pid to the signal queue ..... \n"; 
       $this->signalQueue[$pid] = $status; 
      } 
      $pid = pcntl_waitpid(-1, $status, WNOHANG); 
     } 
     return true; 
    } 
} 
2

puede utilizar "Pthreads"

muy fácil de instalar y funciona muy bien en las ventanas

descarga desde aquí ->http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/

Extrae el archivo zip y luego

  • mueva el archivo 'php_pthreads.dll' al directorio php \ ext \.

  • mueva el archivo 'pthreadVC2.dll' al directorio php \.

luego añadir esta línea en su archivo 'php.ini':

extension=php_pthreads.dll 

guardar el archivo.

que acaba de hacer :-)

Ahora vamos a ver ejemplo de cómo usarlo:

class ChildThread extends Thread { 
    public $data; 

    public function run() { 
     /* Do some expensive work */ 

     $this->data = 'result of expensive work'; 
    } 
} 

$thread = new ChildThread(); 

if ($thread->start()) {  
    /* 
    * Do some expensive work, while already doing other 
    * work in the child thread. 
    */ 

    // wait until thread is finished 
    $thread->join(); 

    // we can now even access $thread->data 
} 

para obtener más información sobre Pthreads leen documentos php aquí:

PHP DOCS PTHREADS

  • si está usando WAMP como yo, entonces debería agregar 'pthreadVC2.dll' en \ wamp \ bin \ apache \ ApacheX.XX \ bin y también editar el archivo 'php.ini' (mismo camino) y añadir la misma línea que antes

    extensión = php_pthreads.dll

¡BUENA SUERTE!

Cuestiones relacionadas