Threads
Thread, que em inglês significa “linha”, representa uma linha de execução de um processo. Para entender melhor este conceito, veremos como funciona a execução de um programa no computador (que ocorre dentro de um processo, como já vimos no artigo anterior).
Ao iniciar um processo, a primeira instrução do programa é lida pela CPU e executada. De acordo com o que está codificado na instrução, a CPU poderá pegar a próxima instrução ou pular para um outro local do programa e continuar. Mas o processo é sempre o mesmo: as instruções vão sendo trazidas para a CPU de acordo com o que havia sido programado pelas instruções anteriores. Ou seja, elas vão sendo executadas sequencialmente.
Este é o conceito de thread: uma linha de execução onde as instruções do programa vão sendo executadas sequencialmente, até que o processo termine. Todo processo tem, pelo menos, uma linha de execução (ou thread). Pode-se dizer com segurança que, até o lançamento do Windows 95 em 1995, a grande maioria dos programas tinham apenas uma thread. Isso fazia com que o conceito de thread praticamente se fundisse em uma característica dos processos.
Programas com múltiplas threads
Ocorre que, muitas vezes, parte dos algoritmos de um programa em execução não exige esta sequência: seria mais vantajoso rodar 2 rotinas paralelamente. Para possibilitar esta execução simultânea de trechos dos programas, os sistemas operacionais implementam chamadas de sistema capazes de iniciar uma segunda (ou terceira, quarta, etc..) thread em um processo que está em execução. Esta técnica é chamada de “multithreading” na literatura técnica.
Mas existe uma forma muito mais simples de executar o mesmo algoritmo em paralelo: basta iniciar o mesmo programa várias vezes. Desta forma é criado um processo para cada vez que o programa é iniciado e todos rodam ao mesmo tempo. Então, qual é a diferença entre este conceito e o conceito de múltiplos processos executando o mesmo programa ao mesmo tempo?
Múltiplos processos versus múltiplas threads
A diferença principal é que ao utilizarmos o “multithreading” dentro do mesmo processo, a memória do processo é compartilhada entre todas as threads. Ao passo que, ao executar várias vezes o mesmo programa, cada processo originado terá sua própria memória isolada dos outros processos.
Além da memória, todos os recursos alocados (como arquivos, dispositivos e portas de comunicação) também são compartilhados por todas as threads do mesmo processo. Em resumo: tudo que uma thread do processo pode acessar, uma outra thread do mesmo processo também pode. E isso é extremamente eficiente pois evita a que seja necessário enviar dados de um processo para outro, uma vez tudo já é acessível a todas as threads.
Outra vantagem importante é que a criação e término de threads em um processo é muito eficiente, bem mais do que a criação e término de processos. Desta forma, sempre que um programa quiser ou precisar rodar parte do algoritmo em paralelo, ele cria uma nova thread para isso sem que a CPU tenha muito trabalho.
“Multithreading” versus “single threading”
Em comparação ao modelo single thread, onde o programa faz todas as operações em uma única thread, há duas vantagens básicas em se utilizar a programação com “multithreading” (e que explicam bem porque esta técnica é tão utilizada atualmente):
Programas com múltiplas threads podem rodar bem mais rápido se a CPU tiver mais que um núcleo, já que é possível executar várias threads simultaneamente. Este conceito será explicado em detalhes abaixo.
Programas que dependem de vários eventos externos, como dados da rede, entradas de usuário, cliques de mouse e outros, são mais fáceis de serem programados neste modelo porque escreve-se várias rotinas diferentes, sendo que cada uma precisa tratar apenas um tipo de evento, colocando-se cada rotina em uma thread diferente.
Desta forma, além das rotinas ficarem pequenas e fáceis de entender, o programa não irá travar caso uma das rotinas fique muito tempo aguardado seu evento.
Recursos da thread
Como mencionado anteriormente, todas as threads compartilham os recursos do processo. Mas isso não quer dizer que uma thread não tenha recursos próprios: cada thread tem sua própria pilha e seu contexto.
A pilha da thread
No contexto dos programas de computador, a pilha é uma estrutura de dados mantida na memória do processo, que é utilizada para viabilizar a execução dos programas escritos utilizando-se linguagens modernas. Este bloco de memória tem 2 funções importantes:
- Manter o valor de todas as variáveis locais, que são as variáveis utilizadas por uma rotina apenas enquanto esta está em execução;
- Manter o caminho percorrido pelo programa até a rotina atual: como se fosse uma trilha de migalhas de pão, esta informação é utilizada para que a CPU possa retornar para o ponto onde o programa estava antes.
Uma vez que a pilha mantém informações sobre a linha de execução, é necessário criar uma pilha nova para cada nova thead do processo, e portanto este é um recurso exclusivo da thread. Disso se conclui também que as operações feitas na pilha pela thread serão invisíveis para as outras threads.
O contexto da thread
Durante o funcionamento do sistema, o computador não executa todas as tarefas disponíveis simultaneamente, e sim alternadamente. É possível executar ao mesmo tempo uma thread em cada núcleo do processador. Ou seja: em uma CPU com dois núcleos pode executar duas threads ao mesmo tempo; as outras tem que ficar em espera.
Estas threads sendo executados simultaneamente podem pertencer ao mesmo processo ou a processos diferentes, mas o fato é que todas as outras threads tem que ficar em espera até serem alocadas na CPU. A parte do sistema operacional responsável por alternar as tarefas na CPU é chamada de “scheduler” ou “escalonador de processos” e é, por si só, um assunto a ser estudado à parte.
Para que uma thread possa ficar neste estado de espera, é necessário guardar em algum lugar todas as informações sobre o que ele estava fazendo quando foi suspenso. Isso inclui o valor de todos os registradores da CPU e outros dados de sistema como o mapeamento da memória do processo ou thread.
Portanto, “contexto da thread” é o nome que se dá a este conjunto de dados que representa o estado exato, permitindo que se reinicie a execução dela sem que nada do que foi feito antes se perca e, inclusive, nem que a própria thread consiga detectar que ficou um tempo fora da CPU.
Condições de concorrência
Muitas vezes quando mais de uma thread acessa ao mesmo tempo uma determinada variável que fica na memória, ocorrem erros no programa.
Imagine o seguinte cenário, onde uma determinada variável é responsável por guardar o número de vezes que uma operação foi feita:
- A primeira thread lê o valor da variável e o incrementa.
- A segunda thread lê o valor da variável, que ainda é o mesmo, e o incrementa.
- A primeira thread escreve de volta o valor incrementado.
- A segunda thread escreve de volta o valor incrementado (que será o mesmo escrito anteriormente).
É fácil notar que no caso acima a variável no final estará incrementada de 1, apesar de ter sido incrementada 2 vezes. Isso ocorre porque as duas threads ficam “concorrendo” pelo direito de incrementar a variável, acabando por interferir no trabalho da outra thread. Este tipo de erro é chamado de “condição de concorrência”.
Numa definição mais formal, “condições de concorrência” são erros de programação que ocorrem quando um determinado recurso (seja uma variável na memória ou um arquivo) é utilizado simultaneamente por mais que um processo ou mais que uma thread no mesmo processo, sem os devidos cuidados.
O caso do incremento, apesar de ser o mais fácil de ser demonstrado, não é o único e nem o mais grave. Resolver este tipo de problema é algo que deve ser feito sempre que decidimos adicionar mais que uma thread a um programa. A solução mais simples consiste em evitar que duas threads fiquem manipulando o mesmo recurso simultaneamente.
Toda semana publicaremos novas matérias sobre Tecnologia, Segurança e Conectividade.
Curta nossa página do Facebook e seja sempre avisado(a) das nossas novidades.
Convidamos também a conhecer nossos produtos, clicando nos sites abaixo.
Obrigado e até a próxima.
Ariel Nigri
Quer melhorar a segurança digital da sua empresa?
A Winco desenvolve há mais de 20 anos soluções de conectividade e segurança digital corporativa para a máxima proteção dos dados da sua empresa. Conheça os nossos produtos:
Winconnection – Solução completa UTM com Filtro de Conteúdo.
Winco Talk Manager – Controle e monitore o Skype e o Eikon Messenger da sua empresa.
Winco DDNS – Acesse facilmente na internet suas câmeras, computadores e outros dispositivos.
Netprotection – Roteador com software de gerenciamento cloud, para sua residência e empresa.
Quer melhorar a segurança digital da sua empresa?
- Winco Firewall – Solução com filtro web, relatórios, hotspot, VPN empresarial e muito mais.
- Winco Talk Manager – Controle e monitore o Skype e o Eikon Messenger da sua empresa.
- Connectas Cloud – VPN Empresarial. Disponibilize acesso seguro e controlado para os colaboradores externos.
- Winco DDNS – Acesse facilmente Câmeras, DVRs, computadores e outros dispositivos.