Параллельное выполнение функций
у меня есть функция, которая должна пройти около 20K строк из массива и применить внешний скрипт к каждому. Это медленный процесс, так как PHP ожидает выполнения скрипта перед продолжением следующей строки.
чтобы сделать этот процесс быстрее, я думал о запуске функции в разных частях одновременно. Так, например, строки от 0 до 2000 как одна функция, от 2001 до 4000 на другой и так далее. Как я могу сделать это аккуратно? Я мог бы ... различные задания, по одному для каждой функции с разными параметрами: myFunction(0, 2000)
, затем еще одно задание cron с myFunction(2001, 4000)
, etc. но это не кажется слишком чистым. Как это сделать?
5 ответов
Если вы хотите выполнять параллельные задачи в PHP, я бы рассмотрел использование Gearman. Другой подход - использовать pcntl_fork (), но я бы предпочел реальных рабочих, когда это задача на основе.
единственное время ожидания вы страдаете между получением данных и обработкой данных. Обработка данных на самом деле полностью блокируется в любом случае (вам просто нужно подождать). Вы вряд ли получите какие-либо преимущества после увеличения количества процессов до количества ядер, которые у вас есть. В основном я думаю, что это означает, что количество процессов невелико, поэтому планирование выполнения 2-8 процессов не звучит так отвратительно. Если вы беспокоитесь о не в состоянии обработать при получении данных теоретически можно получить данные из базы данных небольшими блоками,а затем распределить нагрузку на обработку между несколькими процессами, по одному для каждого ядра.
Я думаю, что я больше согласовываю с подходом к дочерним процессам разветвления для фактического запуска потоков обработки. В комментариях на странице pcntl_fork doc есть блестящая демонстрация, показывающая реализацию демона задания класс!--2-->
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;
}
}
вы можете использовать "PTHREADS"
очень проста в установке и отлично работает на windows
скачать отсюда ->http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/
извлеките zip-файл и затем
переместить файл ' php_pthreads.dll ' в каталог php\ext\.
переместить файл ' pthreadVC2.dll ' в php\ каталог.
затем добавьте эту строку в ваш php.ini ' file:
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 здесь:
-
Если вы используете WAMP, как я, то вы должны добавить 'pthreadVC2.файлов в \wamp\bin\apache\ApacheX.\Х. Х бин а также редактировать ' php.ini ' файл (тот же путь) и добавить ту же строку как и прежде
расширения=php_pthreads.dll файлы
УДАЧИ!
посмотреть pcntl_fork. Это позволяет создавать дочерние процессы, которые затем могут выполнять отдельную работу, которая вам нужна.
Не уверен, что решение для вашей ситуации, но вы можете перенаправить вывод системных вызовов в файл, таким образом, PHP не будет ждать, пока программа не будет завершена. Хотя это может привести к перегрузке сервера.
http://www.php.net/manual/en/function.exec.php - если программа запущена с этой функцией, для того, чтобы продолжать работать в фоновом режиме, вывод программы должен быть перенаправлен в файл или другой поток. Не это приведет к зависанию PHP до завершения выполнения программы.