2012-03-13 12 views
7

Mam funkcję, która musi przekroczyć około 20K wierszy z tablicy i zastosować zewnętrzny skrypt do każdego z nich. Jest to powolny proces, ponieważ PHP czeka na wykonanie skryptu przed kontynuowaniem następnego wiersza.Wykonywanie funkcji równolegle

Aby przyspieszyć ten proces, myślałem o uruchomieniu funkcji w różnych częściach jednocześnie. Na przykład wiersze od 0 do 2000 jako jedna funkcja, od 2001 do 4000 na kolejna itd. Jak mogę to zrobić w zgrabny sposób? Mogłem tworzyć różne zadania cron, po jednej dla każdej funkcji z różnymi parametrami: myFunction(0, 2000), następnie inne zadanie cron z myFunction(2001, 4000) itd., Ale to nie wydaje się zbyt czyste. Jaki jest dobry sposób na robienie tego?

+1

Raz byłem w podobnej sytuacji. Odkryłem, że rozwidlenie było dość bolesne w php, więc zrobiłem to w bash. przekazywał argumenty startu i zatrzymania dla każdej instancji skryptu i uruchamiał je w tle na serwerze. Uważam, że Exec jest tym, czego szukasz. – Jake

Odpowiedz

6

Jeśli chcesz wykonywać równoległe zadania w PHP, rozważam użycie Gearman. Innym podejściem byłoby użycie pcntl_fork(), ale wolałbym rzeczywistych pracowników, gdy jest to oparte na zadaniach.

+0

Jak zasugerowałbyś mnie, gdybym nie miał dostępu do php config (Im 'na współdzielonym serwerze)? – Oliver

0

Spójrz na pcntl_fork. Pozwala to na odradzanie procesów potomnych, które następnie mogą wykonywać oddzielną pracę, której potrzebujesz.

0

Nie jesteś pewien, czy jest rozwiązanie dla twojej sytuacji, ale możesz przekierować wyjście wywołań systemowych do pliku, w ten sposób PHP nie będzie czekać, aż program się zakończy. Chociaż może to spowodować przeciążenie serwera.

http://www.php.net/manual/en/function.exec.php - Jeśli program jest uruchamiany z tą funkcją, aby kontynuował działanie w tle, dane wyjściowe programu muszą zostać przekierowane do pliku lub innego strumienia wyjściowego. Nieprzestrzeganie tego spowoduje, że PHP zawiesi się do czasu zakończenia działania programu.

+0

Link niestety nie działa. –

+0

link zaktualizowany @DaveCarruthers – Michal

6

Jedyny czas oczekiwania dotyczy między uzyskaniem danych i przetworzeniem danych. Przetwarzanie danych jest w rzeczywistości całkowicie blokowane (wystarczy po prostu na nie czekać). Prawdopodobnie nie uzyskasz żadnych korzyści po zwiększeniu liczby procesów do liczby posiadanych rdzeni. Zasadniczo myślę, że oznacza to, że liczba procesów jest mała, więc planowanie realizacji 2-8 procesów nie brzmi tak odrażająco. Jeśli martwisz się, że nie możesz przetwarzać danych podczas pobierania danych, możesz teoretycznie pobrać dane z bazy danych małymi blokami, a następnie rozdzielić obciążenie przetwarzania między kilka procesów, po jednym dla każdego rdzenia.

Myślę, że bardziej wyrównuję podejście do rozwidlania procesów potomnych, aby faktycznie uruchomić wątki przetwarzania. Jest genialny demonstracja w komentarzach na stronie doc pcntl_fork pokazuje implementację klasy demona praca

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

można wykorzystać „Pthreads”

bardzo łatwy w instalacji i działa świetnie na windows

pobrać tutaj ->http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/

Rozpakuj plik zip, a następnie

  • przenieść plik "php_pthreads.dll" do katalogu php \ ext \.

  • przenieść plik "pthreadVC2.dll" do katalogu php \.

następnie dodać ten wiersz w pliku '' php.ini:

extension=php_pthreads.dll 

zapisać plik.

po prostu zrobić :-)

teraz pozwala zobaczyć przykład tego, jak go używać:

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 
} 

więcej informacji o Pthreads czytać dokumenty php tutaj:

PHP DOCS PTHREADS

  • jeśli używasz WAMP jak ja, to powinieneś dodać "pthreadVC2.dll" w \ wamp \ bin \ apache \ bin \ ApacheX.XX a także edytować plik 'php.ini' (tą samą ścieżką) i dodać tę samą linię jak przed

    extension = php_pthreads.dll

DOBRA SZCZĘŚCIE!

Powiązane problemy