Выполнение функций параллельно - PullRequest
9 голосов
/ 13 марта 2012

У меня есть функция, которая должна пройти около 20К строк из массива и применить внешний скрипт к каждому. Это медленный процесс, так как PHP ожидает выполнения скрипта, прежде чем перейти к следующей строке.

Чтобы ускорить этот процесс, я думал о запуске функции в разных частях одновременно. Так, например, строки от 0 до 2000 как одна функция, от 2001 до 4000 для другой и так далее. Как я могу сделать это аккуратно? Я мог бы создавать разные задания cron, по одному для каждой функции с разными параметрами: myFunction(0, 2000), затем другое задание cron с myFunction(2001, 4000) и т. Д., Но это не кажется слишком чистым. Какой хороший способ сделать это?

Ответы [ 5 ]

6 голосов
/ 13 марта 2012

Единственное время ожидания, которое вы испытываете, - это получение данных и их обработка.Обработка данных на самом деле полностью блокирует в любом случае (вам просто нужно подождать).Скорее всего, вы не получите никаких преимуществ после увеличения числа процессов до количества ядер, которые у вас есть.По сути, я думаю, что это означает, что число процессов невелико, поэтому планирование выполнения 2-8 процессов не кажется таким уж отвратительным.Если вас беспокоит невозможность обработки данных во время извлечения данных, теоретически вы можете получить данные из базы данных небольшими блоками, а затем распределить нагрузку по обработке между несколькими процессами, по одному для каждого ядра.

* 1002Я думаю, что я больше согласен с подходом разветвления дочерних процессов для фактического запуска потоков обработки.В комментариях на странице документации pcntl_fork есть блестящая демонстрация, демонстрирующая реализацию класса демона задания

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; 
    } 
}
6 голосов
/ 13 марта 2012

Если вы хотите выполнять параллельные задачи в PHP, я бы подумал об использовании Gearman . Другой подход заключается в использовании pcntl_fork () , но я бы предпочел реальных работников, когда это основано на задачах.

3 голосов
/ 13 апреля 2014

вы можете использовать «PTHREADS»

, очень прост в установке и отлично работает на Windows

скачать здесь -> http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/

Извлеките zip-файл и затем

  • переместить файл 'php_pthreads.dll' в каталог php \ ext \.

  • переместить файл 'pthreadVC2.dll' в php \каталог.

затем добавьте эту строку в ваш файл 'php.ini':

extension=php_pthreads.dll

сохраните файл.

вы только что сделали:-)

теперь давайте посмотрим пример того, как его использовать:

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
}

для получения дополнительной информации о PTHREADS прочитайте php docs здесь:

PHP DOCS PTHREADS

  • если вы используете WAMP, как я, то вам нужно добавить 'pthreadVC2.dll' в \ wamp \ bin \ apache \ ApacheX.XX \ bin и также отредактировать 'php.ini '(тот же путь) и добавьте ту же строку, что и раньше

    extension = php_pthreads.dll

УДАЧИ!

0 голосов
/ 13 марта 2012

Не уверен, если решение для вашей ситуации, но вы можете перенаправить вывод системных вызовов в файл, поэтому PHP не будет ждать, пока программа не будет завершена. Хотя это может привести к перегрузке вашего сервера.

http://www.php.net/manual/en/function.exec.php - Если программа запускается с этой функцией, чтобы она продолжала работать в фоновом режиме, выходные данные программы должны быть перенаправлены в файл или другой поток вывода. В противном случае PHP будет зависать, пока не закончится выполнение программы.

0 голосов
/ 13 марта 2012

Посмотрите на pcntl_fork . Это позволяет вам порождать дочерние процессы, которые затем могут выполнять отдельную работу, которая вам нужна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...