Processamento paralelo com PHP: por que, como e quando

Quando pensamos em PHP, normalmente imaginamos um código síncrono, de thread única, que lida com uma tarefa por vez.
Mas o PHP é mais versátil do que costuma ser considerado. Graças ao suporte ao controle de processos (pcntl), o PHP consegue gerenciar o paralelismo por meio de bifurcação de processos, um conceito nativo de sistemas operacionais do tipo Unix.
Como o suporte ao controle de processos é comum em sistemas UNIX, ele também é compatível com sistemas operacionais semelhantes ao Unix, como Linux e macOS.
O que é multiprocessamento e por que você deve se importar?
O multiprocessamento permite executar múltiplas operações simultaneamente. Isso é especialmente útil quando:
- Você está executando tarefas que exigem muita CPU e que podem ser paralelizadas.
- Você precisa lidar com vários trabalhos em segundo plano em paralelo.
- Você deseja reduzir o tempo de execução dividindo uma tarefa entre vários processos.
- Você está escrevendo scripts PHP de linha de comando onde desempenho e capacidade de resposta são importantes.
Ao escrever scripts CLI (como uma ferramenta de linha de comando), especialmente para tarefas em segundo plano, daemons ou workers, usar
pcntl_fork()
é uma técnica poderosa.
Introdução
pcntl_fork()
: criando processos paralelos em PHP
A
pcntl_fork()
função permite que seu script PHP clone a si mesmo, criando um processo filho que roda em paralelo ao pai. Essa técnica vem diretamente do mundo Unix e abre as portas para o multiprocessamento real em PHP.
Aqui está um exemplo simples que demonstra o uso básico de
pcntl_fork()
:
// Fork the process $pid = pcntl_fork(); if ($pid == -1) { // Fork failed exit("Could not fork process.\n"); } elseif ($pid > 0) { // Parent process echo ' Parent process. PID: ' . getmypid() . "\n"; echo " Created child with PID: $pid\n"; } else { // Child process echo ' Child process. PID: ' . getmypid() . "\n"; sleep(1); // Simulate work echo " Child process done. \n"; exit(0); } // Wait for child to exit pcntl_waitpid($pid, $status); echo " Child {$pid} has finished with status {$status}.\n"; if (pcntl_wifexited($status)) { echo " The child status code represents a normal exit. \n"; echo ' The child returned the code: ' . pcntl_wexitstatus($status) . "\n"; } if (pcntl_wifsignaled($status)) { echo " The child status exit because a siginal. \n"; echo ' The child was terminated by signal code: ' . pcntl_wtermsig($status) . "\n"; }
O que está acontecendo aqui?
- O script se bifurca.
- Os processos pai e filho continuam sendo executados de forma independente.
- O processo filho executa sua lógica e sai.
- O processo pai aguarda o término do processo filho e analisa o status de saída.
Este exemplo oferece controle total e visibilidade sobre como um processo filho se comporta e como o pai pode monitorá-lo ou gerenciá-lo.
Explicação passo a passo
$pid = pcntl_fork();
A
pcntl_fork()
função diz ao sistema operacional: "Crie um clone meu para trabalhar em paralelo".
Após o
pctnl_fork
, dois processos executam o mesmo código. Um processo é o processo pai e o segundo é o processo filho.
Se
pcntl_fork
retornar:
-
-1
, o clone falhou. -
0
, você está dentro do processo filho. - Um PID positivo significa que você ainda é o pai ou a mãe e acabou de obter a identidade da criança.
if ($pid == -1) { exit("Could not fork process.\n"); }
A operação de bifurcação falhou. O processo é encerrado antecipadamente.
elseif ($pid > 0) { echo ' Parent process. PID: ' . getmypid() . "\n"; echo " Created child with PID: $pid\n"; }
Você é o processo pai.
Você imprime seu próprio PID e o ID do novo filho (retornado pela função fork).
else { echo ' Child process. PID: ' . getmypid() . "\n"; sleep(1); // Simulate work echo " Child process done. \n"; exit(0); }
Você é o processo filho!
Esta parte é executada apenas pelo processo filho.
O processo filho dorme por 1 segundo (como se estivesse executando uma tarefa específica), imprime mensagens e depois sai.
exit(0)
é importante. Garante que o filho não execute acidentalmente código destinado apenas ao pai.
pcntl_waitpid($pid, $status);
O processo pai aguarda a conclusão do processo filho.
Esta linha informa ao processo pai para pausar e aguardar até que o processo filho específico (por
$pid
) seja concluído.
echo " Child {$pid} has finished with status {$status}.\n";
Assim que o processo filho termina, o processo pai é notificado com um código de status. Precisamos inspecionar o código de status para entender por que o processo filho encerrou a execução (por exemplo, se concluiu a tarefa corretamente, saiu com erro ou foi interrompido por um sinal do sistema operacional).
if (pcntl_wifexited($status)) { echo " The child status code represents a normal exit. \n"; echo ' The child returned the code: ' . pcntl_wexitstatus($status) . "\n"; }
Verificamos se a criança saiu normalmente.
Em caso positivo, imprimimos o código de retorno (
0
se a criança concluiu com sucesso).
if (pcntl_wifsignaled($status)) { echo " The child status exit because a signal. \n"; echo ' The child was terminated by signal code: ' . pcntl_wtermsig($status) . "\n"; }
Ou talvez a criança tenha sido interrompida.
Isso acontece se um sinal interrompeu o processo (por exemplo,
SIGKILL
,
SIGTERM
).
Resumo do código
Papel | Bloco de código | Descrição |
---|---|---|
Pais | $pid > 0 |
O pai cria e supervisiona a execução da tarefa. |
Criança | $pid == 0 |
O processo filho executa a tarefa e sai. |
Espere | pcntl_waitpid() |
O processo pai aguarda a conclusão do processo filho. |
Verificação de status |
pcntl_wifexited()
,
pcntl_wifsignaled()
|
O processo pai verifica se o processo filho foi concluído corretamente. |
Quando e por que usar forking em PHP
Você pode usar
pcntl_fork()
scripts CLI quando:
- Você deseja executar várias tarefas ao mesmo tempo, como baixar arquivos, processar filas ou executar trabalhos em segundo plano.
- Você está escrevendo um daemon ou sistema de trabalho que precisa escalar e gerenciar tarefas paralelas de forma eficiente.
- Você quer reduzir o tempo de espera realizando tarefas independentes simultaneamente.