Concorrência e Multithreading em PHP: Abordagens Modernas
O PHP é tradicionalmente associado à execução síncrona de código. No entanto, aplicações web modernas exigem o processamento de várias tarefas simultaneamente: desde upload de arquivos até parsing de grandes volumes de dados. Neste artigo, vamos analisar como implementar concorrência e multithreading em PHP, utilizando ferramentas nativas e de terceiros.
O que são concorrência e multithreading?
Concorrência é a capacidade de um programa executar várias tarefas "simultaneamente", alternando entre elas. Multithreading é um caso específico de concorrência, onde as tarefas são executadas em threads separadas do sistema operacional.
No PHP, até a versão 7.4, não havia suporte nativo a threads. No entanto, com o surgimento das extensões pthreads e parallel, além da melhoria no trabalho com processos, a situação mudou.
1. Multithreading com a extensão parallel
A extensão parallel é uma forma moderna de trabalhar com threads no PHP 7.4+. Ela utiliza memória compartilhada, permitindo a troca eficiente de dados entre threads.
Instalação:
pecl install parallelExemplo de criação de thread:
<?phpuse parallel\\Runtime;
$runtime = new Runtime();$future = $runtime->run(function() { // Cálculos pesados $result = 0; for ($i = 0; $i < 1000000; $i++) { $result += sqrt($i); } return $result;});
echo "Thread principal continua trabalhando...";$result = $future->value(); // Obtém o resultadoecho "Resultado: $result";?>Vantagens do parallel:
- Suporte nativo a threads sem dependências extras
- Memória compartilhada entre threads
- Possibilidade de passar closures e objetos
Desvantagens:
- Requer PHP 7.4+ e ZTS (Zend Thread Safety)
- Não é suportado no Windows
- Dificuldades com depuração
2. Assincronia através de processos: pcntl_fork
Se você não precisa de memória compartilhada, pode usar processos com pcntl_fork(). Esta é uma abordagem clássica do Unix.
<?php$pid = pcntl_fork();
if ($pid == -1) { die("Erro ao criar processo");} elseif ($pid) { // Processo pai echo "Pai: aguardando processo filho..."; pcntl_wait($status); echo "Pai: processo filho finalizado";} else { // Processo filho echo "Filho: executando tarefa..."; sleep(3); exit(0);}?>Características do trabalho com processos:
- Cada processo possui sua própria memória
- Para troca de dados, use sockets, memória compartilhada (shmop) ou arquivos
- É necessário tratar processos zumbis com
pcntl_wait()
3. Concorrência através de funções assíncronas e ReactPHP
Para tarefas de I/O (requisições de rede, leitura de arquivos), é mais eficiente usar bibliotecas assíncronas como ReactPHP ou Amp. Elas não criam threads, mas permitem executar tarefas concorrentemente através de um event loop.
Exemplo com ReactPHP:
<?phprequire 'vendor/autoload.php';
use React\\EventLoop\\Factory;use React\\Http\\Browser;
$loop = Factory::create();$client = new Browser($loop);
$promises = [];$urls = [ 'https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3'];
foreach ($urls as $url) { $promises[] = $client->get($url);}
// Aguarda todas as requisições em paralelo$results = [];foreach ($promises as $promise) { $results[] = $promise->wait();}
$loop->run();print_r($results);?>