PHP Multithread

Why is not a good idea to use multithreading in php?

Does forking create a Thread ?

When we fork a process, the process space, that is to say the region of memory where the libraries and code the process requires to execute reside, is duplicated, the distinct but related processes then continue to execute at the will of the operating systems scheduler in different regions of memory.

What is the difference between a Forked Process and a Thread ?

When we create a Thread we are telling the operating system that we want another unit of execution that can operate in the same region of memory as the Process that created it.

How different operating systems actually implement threads and processes is beyond the scope of this answer, and is unimportant.

Why is Forking a bad idea at the frontend ?

When you copy the whole address space, you duplicate the region of memory that the webserver is operating in too, this can obviously cause havoc for your operating system.

Why is Threading a bad idea at the frontend ?

If a client script instructs the operating system to create 8 threads in direct response to a web request, and 100 clients simultaneously request the script, you will be instructing your operating system to execute 800 threads concurrently.

CPUs and operating systems would need to look very very different to make that a good idea!

Where is Threading a good idea?

Multi-threaded software, and extremely capable hardware, is ubiquitous; computing would not be what it is without it.

In the context of Web infrastructure, mysql and other database servers are multi-threaded, indeed Apache can deploy PHP in a multi-threaded infrastructure, though I wouldn't recommend it.

When we look at how enterprising applications like mysql actually provide their extremely complex services, we can see that their process (and therefore threads) are completely isolated from the infrastructure of your web application.

This is how we use Threads in languages that support them; we design systems whose means of providing their services is via some sane form of IPC, we isolate our complex infrastructure, completely, from that which should be simple: our web applications.

Is PHP really suitable for Threads ?

The memory model for PHP is shared nothing: this means that each interpreter context, in the sense of the structures and memory PHP requires to operate, is isolated from any other context.

This always has to be true for PHP to work as intended; an implementation of threading for PHP that was ignorant of the way PHP worked simply would not function.

pthreads goes to great lengths to ensure the memory model is not broken, every Thread does indeed not share memory directly with any other Thread.

Are Threads really suitable for me ?

Firstly, seriously think about the following questions:

  • Is Threading really required ?
  • What other ways can you find to achieve whatever it is you are setting out to do ?

Multi-threaded software is complex by nature; something being complicated is no kind of excuse for avoiding it, in my opinion.

But be aware that multi-threaded software is fundamentally different to your average PHP application, you have to think about things you have never had to think about before, be aware of things that didn't matter before you started your first Thread.

You should not guess at what these things are, you should seek to educate yourself in the subject as thoroughly as possible, and even be prepared to fail, and persevere.

The complexity of anything decreases as your knowledge increases, that's how learning works, here is where it begins:

https://gist.github.com/krakjoe/6437782

It continues in the manual, in the many examples distributed with pthreads, in stackoverflow searches and questions, and results in glory, in my opinion.

Php multithread

There are a few solutions, varying from "Hmmm, just about OK" through to "Poke out your eyes".

  1. Write your multithreaded code as a PHP extension. Probably the most supported, but you need to write in C (or another language supported for extensions).
  2. Spawn child processes onto the underlying OS, and read/write to their input/output with standard file handles. See popen for one route in, or PCNTL. Reasonable, and you can use PHP from the command line.
  3. Make other HTTP requests to yourself via CURL or similar, thus piggybacking on your web servers multi-processing capacity. Keeps all your code "web like", but runs the risk of irate support developers tracking you down and breaking thumbs.

Run asynchronous while functions or multithread

You can read about it(A succinct parallel concurrency API for PHP 7)-> https://github.com/krakjoe/parallel

backend multi-threading in PHP 7 (Symfony4)

There is pthreads extension that is rewritten to be much simpler in use in v3. It is supported on PHP 7.2+ and provides a way to create multi-threaded applications in PHP.

Alternatively since you're using Symfony - you can write simple console command that can use Process component to run sub-processes as separate OS processes. Here is example of such runner from actual project:

<?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);
}
}


Related Topics



Leave a reply



Submit