Como se pode usar o multi threading em aplicativos PHP


414

Existe uma maneira realista de implementar um modelo multiencadeado no PHP, seja verdadeiro ou apenas simulando-o. Algum tempo atrás, foi sugerido que você pudesse forçar o sistema operacional a carregar outra instância do executável PHP e manipular outros processos simultâneos.

O problema é que, quando o código PHP termina de executar a instância do PHP, permanece na memória porque não há como matá-lo no PHP. Então, se você estiver simulando vários threads, pode imaginar o que vai acontecer. Por isso, ainda estou procurando uma maneira de executar ou simular o multi-threading efetivamente no PHP. Alguma ideia?


1
Veja minha pergunta e respostas aqui: stackoverflow.com/questions/2101640/…
powtac



Talvez de interesse: pthreads.org
GibboK

Agora em 2020, parece "paralelo" php.net/manual/en/intro.parallel.php é o que queremos, em vez de "pthreads": stackoverflow.com/a/56451969/470749
Ryan

Respostas:


431

Multi-threading é possível em php

Sim, você pode executar multi-threading em PHP com pthreads

Na documentação do PHP :

O pthreads é uma API orientada a objetos que fornece todas as ferramentas necessárias para o multiencadeamento no PHP. Os aplicativos PHP podem criar, ler, escrever, executar e sincronizar com objetos Threads, Workers e Threaded.

Aviso : A extensão pthreads não pode ser usada em um ambiente de servidor da web. A segmentação em PHP deve, portanto, permanecer apenas em aplicativos baseados em CLI.

Teste simples

#!/usr/bin/php
<?php
class AsyncOperation extends Thread {

    public function __construct($arg) {
        $this->arg = $arg;
    }

    public function run() {
        if ($this->arg) {
            $sleep = mt_rand(1, 10);
            printf('%s: %s  -start -sleeps %d' . "\n", date("g:i:sa"), $this->arg, $sleep);
            sleep($sleep);
            printf('%s: %s  -finish' . "\n", date("g:i:sa"), $this->arg);
        }
    }
}

// Create a array
$stack = array();

//Initiate Multiple Thread
foreach ( range("A", "D") as $i ) {
    $stack[] = new AsyncOperation($i);
}

// Start The Threads
foreach ( $stack as $t ) {
    $t->start();
}

?>

Primeira corrida

12:00:06pm:     A  -start -sleeps 5
12:00:06pm:     B  -start -sleeps 3
12:00:06pm:     C  -start -sleeps 10
12:00:06pm:     D  -start -sleeps 2
12:00:08pm:     D  -finish
12:00:09pm:     B  -finish
12:00:11pm:     A  -finish
12:00:16pm:     C  -finish

Segunda execução

12:01:36pm:     A  -start -sleeps 6
12:01:36pm:     B  -start -sleeps 1
12:01:36pm:     C  -start -sleeps 2
12:01:36pm:     D  -start -sleeps 1
12:01:37pm:     B  -finish
12:01:37pm:     D  -finish
12:01:38pm:     C  -finish
12:01:42pm:     A  -finish

Exemplo do mundo real

error_reporting(E_ALL);
class AsyncWebRequest extends Thread {
    public $url;
    public $data;

    public function __construct($url) {
        $this->url = $url;
    }

    public function run() {
        if (($url = $this->url)) {
            /*
             * If a large amount of data is being requested, you might want to
             * fsockopen and read using usleep in between reads
             */
            $this->data = file_get_contents($url);
        } else
            printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
    }
}

$t = microtime(true);
$g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10));
/* starting synchronization */
if ($g->start()) {
    printf("Request took %f seconds to start ", microtime(true) - $t);
    while ( $g->isRunning() ) {
        echo ".";
        usleep(100);
    }
    if ($g->join()) {
        printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data));
    } else
        printf(" and %f seconds to finish, request failed\n", microtime(true) - $t);
}

3
@ Baaba, não consigo configurar e instalar pthreads no servidor Xampp. Você pode me ajudar com isso?
Irfan

4
Faça o download do binário do Windows aqui windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba

17
Isso é legal, eu não toquei no PHP por anos e agora tem recursos de multithreading!
crizer

1
Agradável e simples! Apenas para sua informação, estou implantando um aplicativo no servidor Azure Cloud Win e se apenas a configuração básica de 1 núcleo for selecionada, o multi-threading não estará disponível, a menos que mais núcleos sejam adicionados.
Milão

3
Cuidado: Joe Watkins, o autor da extensão pthreads, interrompeu o desenvolvimento em favor da nova extensão paralela: github.com/krakjoe/pthreads/issues/929
Anton Belonovich

42

por que você não usa popen ?

for ($i=0; $i<10; $i++) {
    // open ten processes
    for ($j=0; $j<10; $j++) {
        $pipe[$j] = popen('script2.php', 'w');
    }

    // wait for them to finish
    for ($j=0; $j<10; ++$j) {
        pclose($pipe[$j]);
    }
}

4
Estou usando a solução acima e funciona bem, acho que foi a maneira mais fácil de fazer um processo paralelo usando php.
7102 GodFather

7
como o @ e-info128 disse, essa implementação bifurca o processo, o que significa que está sendo executado em um processo diferente e não compartilha recursos do processo. Dito isto, se o trabalho em questão não precisar compartilhar recursos, isso ainda funcionará e será executado em paralelo.
Raffi

1
Como você passaria variáveis ​​para abrir sem usar variáveis ​​de sessão?
Atwellpub 13/03/19

@atwellpub De jeito nenhum, esses são processos separados, que não compartilham recursos. Sessões Mesmo estará mecanismo IPC estranho
Cholthi Paul Ttiopic

1
Para passar dados para eles, você também pode usar argumentos e o servidor Redis.
Amir Fo

21

A segmentação não está disponível no PHP padrão, mas a programação simultânea é possível usando solicitações HTTP como chamadas assíncronas.

Com a configuração de tempo limite da onda definida como 1 e usando o mesmo session_id para os processos que você deseja associar, é possível se comunicar com as variáveis ​​da sessão, como no meu exemplo abaixo. Com esse método, você pode fechar o navegador e o processo simultâneo ainda existe no servidor.

Não se esqueça de verificar o ID da sessão correto como este:

http: //localhost/test/verifysession.php? sessionid = [o ID correto]

startprocess.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo $_REQUEST["PHPSESSID"];

process1.php

set_time_limit(0);

if ($_REQUEST["sessionid"])
   session_id($_REQUEST["sessionid"]);

function checkclose()
{
   global $_SESSION;
   if ($_SESSION["closesession"])
   {
       unset($_SESSION["closesession"]);
       die();
   }
}

while(!$close)
{
   session_start();
   $_SESSION["test"] = rand();
   checkclose();
   session_write_close();
   sleep(5);
}

verifysession.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
var_dump($_SESSION);

closeprocess.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
$_SESSION["closesession"] = true;
var_dump($_SESSION);

4
A última vez que verifiquei (há alguns anos) o php não permitiu acessar o armazenamento de sessões baseado em arquivo por dois processos simultaneamente. Ele bloqueia o arquivo e o segundo processo tem que ficar parado, esperando o primeiro script parar. Estou falando do ambiente do servidor da web, não da CLI.
Alexei Tenitski 5/05

5
set_time_limit(0);caramba! Nunca, nunca faça isso.
Kafoso 27/05

@Kafoso Kafoso porque não? Bem, eu concordo com o PHP como um processador de script da web, mas por que não na CLI? Se algo der errado, CLI podem ser mortos com Ctrl + C ...
sijanec

14

Enquanto você não pode encadear, você tem algum grau de controle de processo no php. Os dois conjuntos de funções que são úteis aqui são:

Funções de controle de processo http://www.php.net/manual/en/ref.pcntl.php

Funções POSIX http://www.php.net/manual/en/ref.posix.php

Você pode bifurcar seu processo com pcntl_fork - retornando o PID da criança. Então você pode usar posix_kill para definir esse PID.

Dito isto, se você matar um processo pai, um sinal deve ser enviado ao processo filho, dizendo para ele morrer. Se o próprio php não estiver reconhecendo isso, você poderá registrar uma função para gerenciá-lo e fazer uma saída limpa usando pcntl_signal.


1
Agora, essa resposta está muito desatualizada (o que é muito justo, sabendo que ela tem 11 anos). Veja pthreads abaixo.
Maciej Paprocki

@MaciejPaprocki pThread agora foi descontinuado do php 7.4 em vez disso use paralelo
Airy


10

Eu sei que essa é uma pergunta antiga, mas para as pessoas que pesquisam, há uma extensão PECL escrita em C que oferece capacidade de multiencadeamento ao PHP agora, está localizada aqui https://github.com/krakjoe/pthreads


O pThread agora foi descontinuado do php 7.4 e, em vez disso, use paralelo
Airy

5

Você pode usar exec () para executar um script de linha de comando (como php de linha de comando) e, se você canalizar a saída para um arquivo, seu script não esperará o término do comando.

Não me lembro bem da sintaxe da CLI do php, mas você gostaria de algo como:

exec("/path/to/php -f '/path/to/file.php' | '/path/to/output.txt'");

Eu acho que muitos servidores de hospedagem compartilhada têm o exec () desativado por padrão por razões de segurança, mas pode valer a pena tentar.


4

Você pode simular a segmentação. O PHP pode executar processos em segundo plano via popen (ou proc_open). Esses processos podem ser comunicados via stdin e stdout. Claro que esses processos podem ser um programa php. Provavelmente é o mais próximo possível.


3

Dependendo do que você está tentando fazer, você também pode usar curl_multi para alcançá-lo.



3

Você pode ter a opção de:

  1. multi_curl
  2. Pode-se usar o comando do sistema para o mesmo
  3. O cenário ideal é criar uma função de encadeamento na linguagem C e compilar / configurar no PHP. Agora essa função será a função do PHP.


3

E o pcntl_fork?

veja nossa página de manual para exemplos: PHP pcntl_fork

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
    }

?>

2

pcntl_forknão funcionará em um ambiente de servidor da web se o modo de segurança estiver ativado. Nesse caso, ele funcionará apenas na versão CLI do PHP.


1

Se você estiver usando um servidor Linux, poderá usar

exec("nohup $php_path path/script.php > /dev/null 2>/dev/null &")

Se você precisar passar alguns argumentos

exec("nohup $php_path path/script.php $args > /dev/null 2>/dev/null &")

Em script.php

$args = $argv[1];

Ou use o Symfony https://symfony.com/doc/current/components/process.html

$process = Process::fromShellCommandline("php ".base_path('script.php'));
$process->setTimeout(0);     
$process->disableOutput();     
$process->start();

-1

No momento da redação do meu comentário atual, eu não sabia sobre os threads do PHP. Eu mesmo procurei a resposta aqui, mas uma solução alternativa é que o programa PHP que recebe a solicitação do servidor da web delega toda a formulação da resposta a um aplicativo de console que armazena sua saída, a resposta à solicitação, em um arquivo binário e o programa PHP que iniciou o aplicativo de console retorna esse arquivo binário byte a byte como resposta à solicitação recebida. O aplicativo de console pode ser escrito em qualquer linguagem de programação executada no servidor, incluindo aqueles que possuem suporte adequado para segmentação, incluindo programas C ++ que usam o OpenMP.

Um truque não confiável e sujo é usar o PHP para executar um aplicativo de console, "uname",

uname -a

e imprima a saída desse comando do console na saída HTML para descobrir a versão exata do software para servidor. Em seguida, instale exatamente a mesma versão do software em uma instância do VirtualBox, compile / monte todos os binários totalmente independentes, de preferência estáticos, desejados e faça o upload deles para o servidor. A partir desse momento, o aplicativo PHP pode usar esses binários na função do aplicativo do console que possui multithread apropriado. É uma solução alternativa suja, não confiável, para uma situação, quando o administrador do servidor não instalou todas as implementações necessárias da linguagem de programação no servidor. É importante observar que, a cada solicitação que o aplicativo PHP recebe, os aplicativos de console terminam / exit / get_killed.

Quanto ao que os administradores do serviço de hospedagem pensam sobre esses padrões de uso do servidor, acho que tudo se resume à cultura. No norte da Europa, o provedor de serviços deve fornecer o que foi anunciado e se a execução de comandos do console foi permitida e o upload de arquivos que não são de malware foi permitido, e o provedor de serviços tem o direito de interromper qualquer processo do servidor após alguns minutos ou até 30 segundos , os administradores do serviço de hospedagem não têm argumentos para formar uma reclamação adequada. Nos Estados Unidos e na Europa Ocidental, a situação / cultura é muito diferente e acredito que há uma grande chance de que, nos EUA e / ou na Europa Ocidental, o provedor de serviços de hospedagem se recuse a atender clientes de serviços de hospedagem que usam o truque descrito acima. Esse é apenas o meu palpite, dada a minha experiência pessoal com os EUA serviços de hospedagem e considerando o que ouvi de outras pessoas sobre serviços de hospedagem na Europa Ocidental. No momento da redação do meu comentário atual (2018_09_01), eu não sabia nada sobre as normas culturais dos provedores de serviços de hospedagem na Europa do Sul, administradores de rede na Europa do Sul.


-3

Pode ser que eu tenha perdido alguma coisa, mas o exec não funcionou como assíncrono para mim no ambiente windows que eu usei seguindo no windows e funcionou como charme;)

$script_exec = "c:/php/php.exe c:/path/my_ascyn_script.php";

pclose(popen("start /B ". $script_exec, "r"));

1
Para hackear multithreading no PHP, você deve abrir várias instruções popen () (o PHP não irá esperar para concluir). Depois que meia dúzia estiver aberta, você chamará pclose () naquelas (o PHP esperará que o pclose () seja concluído). No seu código, você fecha cada segmento imediatamente após abri-lo, para que seu código não se comporte como multithread.
Kmeixner

-5

Multithreading significa executar várias tarefas ou processos simultaneamente, podemos conseguir isso no php usando o código a seguir, embora não exista uma maneira direta de obter multithreading no php, mas podemos alcançar quase os mesmos resultados da seguinte maneira.

chdir(dirname(__FILE__));  //if you want to run this file as cron job
 for ($i = 0; $i < 2; $i += 1){
 exec("php test_1.php $i > test.txt &");
 //this will execute test_1.php and will leave this process executing in the background and will go         

 //to next iteration of the loop immediately without waiting the completion of the script in the   

 //test_1.php , $i  is passed as argument .

}

Test_1.php

$conn=mysql_connect($host,$user,$pass);
$db=mysql_select_db($db);
$i = $argv[1];  //this is the argument passed from index.php file
for($j = 0;$j<5000; $j ++)
{
mysql_query("insert  into  test   set

                id='$i',

                comment='test',

                datetime=NOW() ");

}

Isso executará test_1.php duas vezes simultaneamente e os dois processos serão executados em segundo plano simultaneamente, para que você possa obter multithreading em php.

Esse cara fez um ótimo trabalho Multithreading em php


Além disso, isso não tem nada a ver com o MultiThreading. Isso é processamento paralelo. Coisas totalmente diferentes.
Digital Human

Na minha opinião, como uma solução alternativa, um hack de emergência, a idéia por trás da solução oferecida é muito apropriada, mas acho que diferentes pessoas podem ter uma guerra de chamas sobre o que constitui "verdadeiro multi-threading", porque há uma distinção entre concorrência e hardware com base em processamento paralelo, conforme descrito em: youtube.com/watch?v=cN_DpYBzKso
Martin Vahi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.