php 并行执行函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9684290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Executing functions in parallel
提问by luqita
I have a function that needs to go over around 20K rows from an array, and apply an external script to each. This is a slow process, as PHP is waiting for the script to be executed before continuing with the next row.
我有一个函数需要遍历数组中的大约 20K 行,并将外部脚本应用于每个行。这是一个缓慢的过程,因为 PHP 在继续下一行之前正在等待脚本执行。
In order to make this process faster I was thinking on running the function in different parts, at the same time. So, for example, rows 0 to 2000 as one function, 2001 to 4000 on another one, and so on. How can I do this in a neat way? I could make different cron jobs, one for each function with different params: myFunction(0, 2000)
, then another cron job with myFunction(2001, 4000)
, etc. but that doesn't seem too clean. What's a good way of doing this?
为了使这个过程更快,我正在考虑同时在不同部分运行该功能。因此,例如,第 0 行到 2000 行作为一个函数,2001 到 4000 行作为另一个函数,依此类推。我怎样才能以一种整洁的方式做到这一点?我可以制作不同的 cron 作业,每个函数使用不同的 params: myFunction(0, 2000)
,然后是另一个 cron 作业myFunction(2001, 4000)
,等等,但这似乎不太干净。这样做的好方法是什么?
采纳答案by Bj?rn
If you'd like to execute parallel tasks in PHP, I would consider using Gearman. Another approach would be to use pcntl_fork(), but I'd prefer actual workers when it's task based.
如果您想在 PHP 中执行并行任务,我会考虑使用Gearman。另一种方法是使用pcntl_fork(),但当它基于任务时,我更喜欢实际的工人。
回答by Matt Esch
The only waiting time you suffer is between getting the data and processing the data. Processing the data is actually completely blocking anyway (you just simply have to wait for it). You will not likely gain any benefits past increasing the number of processes to the number of cores that you have. Basically I think this means the number of processes is small so scheduling the execution of 2-8 processes doesn't sound that hideous. If you are worried about not being able to process data while retrieving data, you could in theory get your data from the database in small blocks, and then distribute the processing load between a few processes, one for each core.
您遭受的唯一等待时间是在获取数据和处理数据之间。无论如何,处理数据实际上是完全阻塞的(你只需要等待它)。通过将进程数量增加到您拥有的核心数量,您不太可能获得任何好处。基本上我认为这意味着进程的数量很少,所以安排 2-8 个进程的执行听起来并不那么可怕。如果您担心在检索数据时无法处理数据,理论上您可以以小块的形式从数据库中获取数据,然后在几个进程之间分配处理负载,每个内核一个。
I think I align more with the forking child processes approach for actually running the processing threads. There is a brilliant demonstration in the comments on the pcntl_fork doc page showing an implementation of a job daemon class
我认为我更符合实际运行处理线程的分叉子进程方法。pcntl_fork 文档页面的评论中有一个精彩的演示,展示了作业守护程序类的实现
http://php.net/manual/en/function.pcntl-fork.php
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;
}
}
回答by Danny Michaeli
you can use "PTHREADS"
你可以使用“PTHREADS”
very easy to install and works great on windows
非常容易安装并且在 Windows 上运行良好
download from here -> http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/
从这里下载-> http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/
Extract the zip file and then
解压 zip 文件,然后
move the file 'php_pthreads.dll' to php\ext\ directory.
move the file 'pthreadVC2.dll' to php\ directory.
将文件 'php_pthreads.dll' 移动到 php\ext\ 目录。
将文件“pthreadVC2.dll”移动到 php\ 目录。
then add this line in your 'php.ini' file:
然后在你的“php.ini”文件中添加这一行:
extension=php_pthreads.dll
save the file.
保存文件。
you just done :-)
你刚刚完成:-)
now lets see example of how to use it:
现在让我们看看如何使用它的例子:
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
}
for more information about PTHREADS read php docs here:
有关 PTHREADS 的更多信息,请在此处阅读 php 文档:
if you'r using WAMP like me, then you should add 'pthreadVC2.dll' into \wamp\bin\apache\ApacheX.X.X\bin and also edit the 'php.ini' file (same path) and add the same line as before
extension=php_pthreads.dll
如果你像我一样使用 WAMP,那么你应该将 'pthreadVC2.dll' 添加到 \wamp\bin\apache\ApacheX.XX\bin 并编辑 'php.ini' 文件(相同路径)并添加相同的行像以前一样
扩展=php_pthreads.dll
GOOD LUCK!
祝你好运!
回答by Harry
What you are looking for is parallel
你要找的是平行的
Parallel is a succinct parallel concurrency API for PHP 7+.
Parallel 是一个简洁的 PHP 7+ 并行并发 API。
$runtime = new \parallel\Runtime();
$future = $runtime->run(function(){
for ($i = 0; $i < 500; $i++)
echo "*";
return "easy";
});
for ($i = 0; $i < 500; $i++) {
echo ".";
}
printf("\nUsing \parallel\Runtime is %s\n", $future->value());
Output:
输出:
.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*
Using \parallel\Runtime is easy
回答by Michal
Not sure if a solution for your situation but you can redirect the output of system calls to a file, thus PHP will not wait until the program is finished. Although this may result in overloading your server.
不确定是否有适合您情况的解决方案,但您可以将系统调用的输出重定向到文件,因此 PHP 不会等到程序完成。尽管这可能会导致您的服务器超载。
http://www.php.net/manual/en/function.exec.php- If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
http://www.php.net/manual/en/function.exec.php-如果一个程序用这个函数启动,为了让它在后台继续运行,程序的输出必须重定向到文件或其他输出流。如果不这样做,将导致 PHP 挂起,直到程序执行结束。
回答by Jim
Have a look at pcntl_fork. This allows you to spawn child processes which can then do the separate work that you need.
看看pcntl_fork。这允许您生成子进程,然后可以执行您需要的单独工作。