Multi-thread, controle de processos e programação paralela em PHP
Author: Ricardo Soares - Postado em: 13/10/2010
Relacionado as categorias: Planeta PHP, Tecnologia | 3 Comments
Um amigo perguntou em uma lista que participo sobre thread programming em PHP, bem …, em php esta técnica também é conhecida como programação em paralelo (porem cuidado pois programação em paralelo em linguagens como Asembly e C é outra coisa), ou Controle de Processos (Process Control que em engenharia também tem outro significado).
Programação multi-thread é algo tão simples que o próprio conceito já se explica com o próprio nome, o problema para isto é que não existe uma tradução direta para a palavra “thread” (no sentido utilizado) no português, recomendo que quando falando sobre o assunto utilizem o termo thread pois é justamente isto que o programa faz ele cria várias “processos” originados de um único pai, é difícil encontrar exemplos de programação em thread com php porque seu uso é frequênte em softwares servidores, não estou falando que é algo inútil, apenas que geralmente seu uso em softwares, mesmo em outras linguagens como C e Assembly, é muito pouco explorado. Com o advendo de chips multicore seu uso ganhou uma luz, porem ainda assim são poucos os aplicativos que demandam deste tipo de recurso.
Eu fiz um programa a algum tempo que seria o esboço de um servidor de e-mail, seria um programa que iria substituir aplicativos como o qmail, postfix e sendmail, porem no final não demos continuidade pois conseguimos os recursos necessários com os aplicativos que possuíamos, o conceito de multi-thread é bem simples, você tem o pai que gera quantos filhos forem demandados, ele gerencia estes filhos e os filhos executam os processos do programa, neste exemplo enquanto o thread 1 ficava aguardando um cliente se conectar o thread 2 já estava enviando ou recebendo algum e-mail, se não fosse feito uma programação multi-thread o aplicativo só poderia atender um único cliente por vez. Com relação ao thread programming o que me complicou é que tive que construir todo um controle para garantir que a quantidade de filhos existentes no ambiente não excedesse o limite disponível dos recursos de hardware.
Algumas observações sobre programação em threads
- PHP não possui suporte “built-in” para multithreading, isto é a realidade, multithread em fato é implementado pela biblioteca PCNTL que não é funcional em todos os sistemas (Windows XP em diante se não me engano passou a virtualizar multithread de forma a disponibilizar esta funcionalidade para esta plataforma e atualmente a maior parte dos sistemas operacionais/hardwares suportam multithread), porem boa parte das instalações atuais de PHP disponibilizam multithread.
- Se o thread 1 criou uma variável esta não está disponível para o thread 2, ela é exclusiva ao thread 1, o Thread 2 pode até criar uma variável com o mesmo nome, porem seu conteúdo no thread 1 não será acessível pelo thread 2, seu endereçamento de memória será diferente, caso você precise compartilhar informações entre os threads é prática comum (em qualquer linguagem) o uso de memória compartilhada (Em PHP a biblioteca mais popular para compartilhamento de memória é as funções SHM_, como p. ex., shm_attach);
- Uma variável criada em um thread pai é automaticamente existente no thread filho, porem qualquer alteração em abas as partes não é percebida em sua contra-parte, ou seja, se você criar a variável $a no thread pai esta vai estar disponível no thread filho, porem se você alterar esta variável no thread pai esta alteração não será sentida pelo thread filho, ao se instanciar um thread filho todas as variáveis do pai são copiadas em memória e disponibilizadas para o thread filho, é por isto que geralmente processos pai são limpos e leves, para serem rápidos ao instanciar filhos.
- Você pode criar um while para criar e gerenciar threads, mas cuidado com as limitações de hardware, lembre-se que todos os limites são únicos para cada processo, em outras palavras, se você possui 10M de limite de alocação de memória este será estendido ao thread, ou seja, digamos que você tenha um processo pai e dois filhos, seu aplicativo pode então alocar até 30M de memória.
- Cada processo (o pai e cada um dos filhos) é um processo único para o sistema operacional, mesmo para ambientes que virtualizam multiprocessamento utilizando chips mono core cada thread é um processo em separado no sistema operacional, assim como o pai que também é um processo único.
- Assim como qualquer outro aplicativo multi-thead, caso você venha a matar um thread este não fará diferença para os outros threads (a não ser que este seja um thread que esteja efetuando algum cálculo e irá entregar os resultados para outro thread, neste caso você ficará sem o resultado deste thread), porem se você matar o thread pai (também conhecido simplesmente como pai ou ainda thread principal) este irá matar todos os demais threads originados nele;
- Você pode criar um thread filho e a partir deste thread criar um outro thread filho, não existem limitações de filhos porem é muito difícil encontrar um aplicativo que construa um terceiro nível de hierarquia pela dificuldade existente no controle desta.
- Multithread não é muito útil para aplicativos construídos para rodarem sobre o apache, não adianta pensar em utilizar o multicore do processador posto que outro thread do próprio apache já estará utilizando aquela parte do processamento, é como tentar rodar mais rápido o processo 1 tirando o processador do processo 2, o processo 2 também precisa ser finalizado, porem um bom exemplo de como utilizar multithread em php é o phpUnit, você pode construir uma suite e separar seu processamento em várias instâncias para agilizar a conclusão gravando em disco os resultados, recentemente Sebastian Bergmann (o criador do phpUnit) escreveu um tweet sobre um teste sendo executado em uma máquina com 16 cores http://twitter.com/#!/s_bergmann/status/26773317109
Em minha opinião, o que acaba ficando legal de estudar é a parte histórica do motivo disto. Você vai encontrar muito material referente a linguagem C http://softpixel.com/~cwright/programming/threads/threads.c.php
Abaixo um exemplo simples de código utilizando inclusive as funcionalidades de memória compartilhada
<?php define('FOPEN_RESOURCE', 1); $shm_id = shm_attach(FOPEN_RESOURCE); if ($shm_id === false) { exit('Fail to attach shared memory.'.PHP_EOL); } // efetua a primeira gravação onde vai ter um valor inicial $a = array('Teste1', 1); if (!shm_put_var($shm_id, $a, $a)) { exit('Failed to put var 1 in shared memory $shm_id.'.PHP_EOL); } echo 'P: '.getmypid().' '.$a[0].':'.$a[1].PHP_EOL; $pid = pcntl_fork(); if($pid == -1) { die('could not fork'.PHP_EOL); } else if ($pid) { $a = array('Teste2', 3); // altera o valor inicial compartilhado if (!shm_put_var($shm_id, $a, $a)) { exit('Failed to put var 1 in shared memory $shm_id.'.PHP_EOL); } echo 'P: '.getmypid().' '.$a[0].':'.$a[1].PHP_EOL; } else { sleep(2); // efetua a leitura do valor alterado no thread pai $a = shm_get_var($shm_id, $a); echo 'F: '.getmypid().' '.$a[0].':'.$a[1].PHP_EOL; } pcntl_wait($status); exit();
Atualizações:
- 01/mai/19: Recentemente um rapaz se surpreendeu ao saber que PHP tinha capacidade multithread! Eu envolvido na conversa erroneamente o desorientei, falando que tinha, porem quem tem capacidade de multiprocessar algo é o hardware, o que de certa forma expliquei para ele, mas que também não deixo claro neste artigo, como o artigo é para iniciantes deixo aqui esta referência.
Comments
3 Responses to “Multi-thread, controle de processos e programação paralela em PHP”
Leave a Reply
Caso você execute algum comando do sistema operacional com as funções ao estilo system, popen ou passthru este comando será filho de seu processo princial, isto ocorre mesmo não utilizando as funções pcntl.
Se você efetuar uma chamada com o comando “pstree -acp ” repassando o PID do pai, O sistema irá apresentar para você algo similar ao abaixo:
php,5109 sender.php
|-php,5121 sender.php
|-php,5110 sender.php
`-pstree,5122 -acp 5109
Muito bom artigo. Embora vc tenha dito que não há ganho real ao usar multi-threads sob o apache, acho que podemos usar a imaginacao combinando PHP+MTHREADS+RPC.
Mais uma vez parabéns pelo artigo.
MUUITO BOM !!!