Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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
PHP7(Symfony4)中的后端多线程_Php_Multithreading_Symfony_Php 7_Symfony4 - Fatal编程技术网

PHP7(Symfony4)中的后端多线程

PHP7(Symfony4)中的后端多线程,php,multithreading,symfony,php-7,symfony4,Php,Multithreading,Symfony,Php 7,Symfony4,(我读过其他问题,但它们指的是旧版本的PHP或前端多线程) 我有一个PHP/PostgreSQL应用程序,它有一个复杂的后端处理部分。本质上,有一个非常大的循环(几千次迭代)一遍又一遍地遍历相同的数据(使用排列)。在每个循环中,读取相同的数据,应用操作,将结果写回数据库。这些循环彼此完全独立,循环之间不保留任何结果。事实上,为了清除对象缓存内存(使用原则),我每隔100个左右循环清除一次缓存 所以我基本上有: for ($i=0; $i<5000; $i++) { // fetch

(我读过其他问题,但它们指的是旧版本的PHP或前端多线程)

我有一个PHP/PostgreSQL应用程序,它有一个复杂的后端处理部分。本质上,有一个非常大的循环(几千次迭代)一遍又一遍地遍历相同的数据(使用排列)。在每个循环中,读取相同的数据,应用操作,将结果写回数据库。这些循环彼此完全独立,循环之间不保留任何结果。事实上,为了清除对象缓存内存(使用原则),我每隔100个左右循环清除一次缓存

所以我基本上有:

for ($i=0; $i<5000; $i++) {
   // fetch data
   // manipulate data
   // write results to a different table
}
对于($i=0;$i有一个扩展,在v3中被重写为使用更简单。它在PHP7.2+上受支持,并提供了一种在PHP中创建多线程应用程序的方法

或者,由于您使用的是Symfony,您可以编写简单的控制台命令,该命令可以使用组件将子进程作为单独的操作系统进程运行。下面是实际项目中的此类运行程序示例:

<?php

namespace App\Command;

use App\Command\Exception\StopCommandException;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use Webmozart\PathUtil\Path;

class ProcessingRunner extends AbstractCommand
{
    use LockableTrait;
    /**
     * @var Process[]
     */
    private $processes = [];
    /**
     * @var string[]
     */
    private $cmd;
    /**
     * @var KernelInterface
     */
    private $kernel;

    /**
     * @param KernelInterface $kernel
     */
    public function __construct(KernelInterface $kernel)
    {
        parent::__construct();
        $this->kernel = $kernel;
    }

    /**
     * {@inheritdoc}
     * @throws InvalidArgumentException
     */
    protected function configure(): void
    {
        $this
            ->setName('app:processing:runner')
            ->setDescription('Run processing into multiple threads')
            ->addOption('threads', 't', InputOption::VALUE_REQUIRED, 'Number of threads to run at once', 1)
            ->addOption('at-once', 'm', InputOption::VALUE_REQUIRED, 'Amount of items to process at once', 10);
    }

    /**
     * {@inheritdoc}
     * @throws \Symfony\Component\Process\Exception\LogicException
     * @throws InvalidArgumentException
     * @throws RuntimeException
     * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
     * @throws \InvalidArgumentException
     * @throws \LogicException
     */
    protected function execute(InputInterface $input, OutputInterface $output): ?int
    {
        if (!$this->lock()) {
            $output->writeln('The command is already running in another process.');
            return 0;
        }
        if (extension_loaded('pcntl')) {
            $stop = function () {
                StopCommandException::throw();
            };
            pcntl_signal(SIGTERM, $stop);
            pcntl_signal(SIGINT, $stop);
            pcntl_async_signals(true);
        }
        do {
            try {
                while (\count($this->processes) < $this->getInput()->getOption('threads')) {
                    $process = $this->createProcess();
                    $process->start();
                    $this->processes[] = $process;
                }
                $this->processes = array_filter($this->processes, function (Process $p) {
                    return $p->isRunning();
                });
                usleep(1000);
            } catch (StopCommandException $e) {
                try {
                    defined('SIGKILL') || define('SIGKILL', 9);
                    array_map(function (Process $p) {
                        $p->signal(SIGKILL);
                    }, $this->processes);
                } catch (\Throwable $e) {

                }
                break;
            }
        } while (true);
        $this->release();
        return 0;
    }

    /**
     * @return Process
     * @throws RuntimeException
     * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
     * @throws \InvalidArgumentException
     * @throws \LogicException
     * @throws InvalidArgumentException
     */
    private function createProcess(): Process
    {
        if (!$this->cmd) {
            $phpBinaryPath = (new PhpExecutableFinder())->find();
            $this->cmd = [
                $phpBinaryPath,
                '-f',
                Path::makeAbsolute('bin/console', $this->kernel->getProjectDir()),
                '--',
                'app:processing:worker',
                '-e',
                $this->kernel->getEnvironment(),
                '-m',
                $this->getInput()->getOption('at-once'),
            ];
        }
        return new Process($this->cmd);
    }
}

看看这篇文章,像bashshell这样简单的东西能产生多个进程吗?(或GNU并行)@Progrock这是一个替代方案,但我可能想稍后添加一个选项,从前端触发后端脚本,并进行进度更新等。今天@mcek发现这个,它实际上是非常空的:
class StopCommandException extends\RuntimeException{public static function throw():void{throw new self(“”);}
已经两年多了,没有比这更好的答案了。虽然这项工作比我希望的要多,但似乎可行,所以我接受了。