Um dos aspectos mais interessantes a respeito do uso de threads (ou linhas de execução) em um programa é a possibilidade que se abre para divisão real de tarefas dentro do programa. Se, para resolver um problema computacional, é possível dividir uma tarefa grande em passos menores que podem ser executados simultaneamente, então a resolução desse problema pode ser implementada com threads. Num computador com múltiplos núcleos (CPUs), essa solução significará uma execução mais rápida, com um tempo de resposta menor já que a execução das threads nesse ambiente será de fato simultânea.
Uma situação real em que isso pode ser aplicado é quando você tem massas de dados heterogênas (vindas de fontes diferentes) e que precisam ser ordenadas antes de serem utilizadas como se viessem uma única fonte.
Logo de cara, podemos identificar um grande problema : eu não sei de antemão o tamanho dessas fontes de dados, isto é, quantos registros poderão vir de cada fonte. Só é possível prosseguir com o processamento após todas as threads terminarem a sua parte na busca e ordenação. Assim, certamente teremos que sincronizar essas threads para que o uso da lista final ordenada só ocorra depois do fim da última thread de ordenação.
O mecanismo que os sistemas operacionais disponibilizam para esse fim é chamado Evento. No Delphi, a classe TEvent implementa esse mecanismo, encapsulando chamadas à API do Windows. Um objeto do tipo Evento é um objeto de sincronização cujo estado pode ser explicitamente sinalizado, isto é, o programador pode indicar manualmente que ocorreu um determinado evento que outras threads estejam aguardando.
Em termos de programação, é preciso criar um objeto global do tipo Evento e dar-lhe um nome conhecido que o diferencie de outros Eventos que possam existir. O objeto tem que ser global - e não parte de uma thread particular - porque ele terá que ser acessível por outras threads do programa. Ao dar início a cada uma das threads que queremos aguardar, o programa incrementa um contador. Uma thread em especial, então, ficará em estado suspenso, aguardando a notificação de ocorrência do evento. Conforme cada thread vai sendo finalizada, o contador é decrementado. Na última delas, o contador chegará a zero e a thread pode emitir a notificação de que todas já completaram seus respectivos processamentos e o fluxo de execução da tarefa pode continuar. Veja a representação desse funcionamento no gráfico abaixo:
A thread que vai aguardar a sinalização pode ser a principal do seu programa. Entretanto, isso não é recomendável porque é a thread principal quem recebe as mensagens do Windows, incluindo aquelas resultantes de interação com o usuário. Portanto, ao ter sua execução suspensa, o programa perderá interatividade e dará ao usuário a impressão de que está "travado".
É claro que neste tipo de cenário devemos dobrar a atenção no tratamento de erros e exceções no processamento das threads, caso contrário a sinalização do evento pode nunca ocorrer e o programa aguardará indefinidamente.
Volto num próximo post com código em Delphi para implementar o exemplo.
2 comentários :
Olá Luiz Gustavo,
Achei excelente sua explanação sobre threads e a utilização de eventos quando se tem massas de dados heterogêneas e que precisam ser ordenadas!
Eu só não achei no seu blog o exemplo prático, vc chegou a faze-lo?
Abraços
Rafael
O exemplo de uso da API de sincronização está no post Trabalhando com Threads em Delphi - Sincronização com Eventos - parte 2.
[]s
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.