Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/249.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在PHP CLI中使用并行线程运行大循环_Php_Multithreading_Symfony - Fatal编程技术网

在PHP CLI中使用并行线程运行大循环

在PHP CLI中使用并行线程运行大循环,php,multithreading,symfony,Php,Multithreading,Symfony,我在Symfony2/PHP中有一个昂贵的计算后端进程,我想运行多线程 因为我迭代了数千个对象,所以我认为我不应该为每个对象启动一个线程。我希望有一个$cores变量来定义我希望并行的线程数量,然后在循环中迭代并保持这些线程的运行。因此,每次线程结束时,都应该启动一个包含下一个对象的新线程,直到所有对象都完成为止 查看pthreads文档并进行一些谷歌搜索,我找不到适用于这种情况的示例。我发现的所有示例都有固定数量的线程,它们一次运行一次,没有一个迭代数千个对象 有人能给我指出正确的开始方向吗?

我在Symfony2/PHP中有一个昂贵的计算后端进程,我想运行多线程

因为我迭代了数千个对象,所以我认为我不应该为每个对象启动一个线程。我希望有一个$cores变量来定义我希望并行的线程数量,然后在循环中迭代并保持这些线程的运行。因此,每次线程结束时,都应该启动一个包含下一个对象的新线程,直到所有对象都完成为止

查看pthreads文档并进行一些谷歌搜索,我找不到适用于这种情况的示例。我发现的所有示例都有固定数量的线程,它们一次运行一次,没有一个迭代数千个对象


有人能给我指出正确的开始方向吗?我了解设置线程和连接线程等的基本知识,但不了解如何在具有等待条件的循环中执行。

问题的答案是使用
工作者
抽象

基本思想是您
::将
线程化的
对象提交到
,它将该对象堆叠到下一个可用的
工作者
,将
线程化的
对象(循环)分布到所有
工作者

下面是针对PHP7(pthreads v3)的超级简单代码:


显然,这些工作毫无意义。在现实世界中,我猜您的
$jobs
数组一直在增长,因此您可以在
期间将
foreach
交换一些
do{},然后继续调用
::submit
,以获取新作业

在现实世界中,您需要在同一个循环中收集垃圾(只需调用
Pool::collect
,而不使用默认行为参数)

值得注意的是,如果PHP真的不打算在多线程环境中工作,那么这一切都是不可能的。。。它肯定是

这是问题的答案,但这并不能使它成为解决问题的最佳方案

您在评论中提到,您假设执行Symfony代码的8个线程占用的内存少于8个进程。事实并非如此,PHP一直没有共享任何内容。您可以期望8个Symfony线程占用的内存与8个Symfony进程占用的内存一样多,事实上,占用的内存稍微多一点。在进程上使用线程的好处是,它们可以相互通信、同步和(似乎)共享

仅仅因为你可以,并不意味着你应该。对于手头的任务,最好的解决方案可能是使用一些现成的软件包或软件来完成所需的工作

充分研究这些东西以实现一个健壮的解决方案需要很长时间,而且您不想部署第一个解决方案


如果您决定忽略我的建议,试一试,您可以在github pthreads存储库中找到许多方法。

Joe有一个很好的方法,但我在其他地方找到了一个不同的解决方案,我现在正在使用。基本上,我有两个命令,一个控件和一个工作者命令。control命令启动后台进程并检查其结果:

protected function process($worker, $entity, $timeout=60) {
    $min = $this->em->createQuery('SELECT MIN(e.id) FROM BM2SiteBundle:'.$entity.' e')->getSingleScalarResult();
    $max = $this->em->createQuery('SELECT MAX(e.id) FROM BM2SiteBundle:'.$entity.' e')->getSingleScalarResult();

    $batch_size = ceil((($max-$min)+1)/$this->parallel);
    $pool = array();
    for ($i=$min; $i<=$max; $i+=$batch_size) {
        $builder = new ProcessBuilder();
        $builder->setPrefix($this->getApplication()->getKernel()->getRootDir().'/console');
        $builder->setArguments(array(
            '--env='.$this->getApplication()->getKernel()->getEnvironment(),
            'maf:worker:'.$worker,
            $i, $i+$batch_size-1
            ));
        $builder->setTimeout($timeout);

        $process = $builder->getProcess();
        $process->start();
        $pool[] = $process;
    }
    $this->output->writeln($worker.": started ".count($pool)." jobs");
    $running = 99;
    while ($running > 0) {
        $running = 0;
        foreach ($pool as $p) {
            if ($p->isRunning()) {
                $running++;
            }
        }
        usleep(250);
    }

    foreach ($pool as $p) {
        if (!$p->isSuccessful()) {
            $this->output->writeln('fail: '.$p->getExitCode().' / '.$p->getCommandLine());
            $this->output->writeln($p->getOutput());
        }
    }

}
受保护的功能进程($worker、$entity、$timeout=60){
$min=$this->em->createQuery('SELECT min(e.id)FROM BM2SiteBundle:'.$entity.e')->getSingleScalarResult();
$max=$this->em->createQuery('SELECT max(e.id)FROM BM2SiteBundle:'.$entity.e')->getSingleScalarResult();
$batch_size=ceil(($max-$min)+1)/$this->parallel);
$pool=array();
对于($i=$min;$isetPrefix($this->getApplication()->getKernel()->getRootDir()。/console');
$builder->setArguments(数组(
“--env=”。$this->getApplication()->getKernel()->getEnvironment(),
“maf:worker:”.$worker,
$i,$i+$batch\u size-1
));
$builder->setTimeout($timeout);
$process=$builder->getProcess();
$process->start();
$pool[]=$process;
}
$this->output->writeln($worker.”:start“.count($pool)。“jobs”);
$running=99;
同时($running>0){
$running=0;
foreach(池为$p){
如果($p->isRunning()){
$running++;
}
}
usleep(250);
}
foreach(池为$p){
如果(!$p->isSuccessful()){
$this->output->writeln('fail:'.$p->getExitCode()./'.$p->getCommandLine());
$this->output->writeln($p->getOutput());
}
}
}
其中$this->parallel是我在8核机器上设置为6的变量,它表示要启动的进程数。请注意,此方法要求我迭代一个特定的实体(它被该实体拆分),这在我的用例中总是正确的

它并不完美,但它开始了全新的进程而不是线程,我认为这是更好的解决方案。

worker命令接受min和max ID编号,并对这两个编号之间的集合执行实际工作


只要数据集分布合理,这种方法就可以工作。如果没有1-1000范围内的数据,但使用了1000到2000之间的每个ID,则前三个进程将没有任何作用。

php并不是真正为多线程设计的,pthreads文档对此有明显的警告。更简单的选择是将所有任务放入一个队列中,然后启动x个进程(exec)为队列提供服务。另一个选项是将任务分成若干组(taskcount/x),然后再次启动x进程,将一个组传递给每个进程不确定为给定数量的对象指定一个线程是否是一个好的选择,如果您有很多对象,您将有很多线程,并且您的程序将变慢而不是变快。这就是为什么在您发现的示例中线程是固定的。我可以向您建议这一点吗?我是mantainer,我使用了Symonfy流程;这不是您的用例,但也许它可以让您领先…或者您可以采取
protected function process($worker, $entity, $timeout=60) {
    $min = $this->em->createQuery('SELECT MIN(e.id) FROM BM2SiteBundle:'.$entity.' e')->getSingleScalarResult();
    $max = $this->em->createQuery('SELECT MAX(e.id) FROM BM2SiteBundle:'.$entity.' e')->getSingleScalarResult();

    $batch_size = ceil((($max-$min)+1)/$this->parallel);
    $pool = array();
    for ($i=$min; $i<=$max; $i+=$batch_size) {
        $builder = new ProcessBuilder();
        $builder->setPrefix($this->getApplication()->getKernel()->getRootDir().'/console');
        $builder->setArguments(array(
            '--env='.$this->getApplication()->getKernel()->getEnvironment(),
            'maf:worker:'.$worker,
            $i, $i+$batch_size-1
            ));
        $builder->setTimeout($timeout);

        $process = $builder->getProcess();
        $process->start();
        $pool[] = $process;
    }
    $this->output->writeln($worker.": started ".count($pool)." jobs");
    $running = 99;
    while ($running > 0) {
        $running = 0;
        foreach ($pool as $p) {
            if ($p->isRunning()) {
                $running++;
            }
        }
        usleep(250);
    }

    foreach ($pool as $p) {
        if (!$p->isSuccessful()) {
            $this->output->writeln('fail: '.$p->getExitCode().' / '.$p->getCommandLine());
            $this->output->writeln($p->getOutput());
        }
    }

}