1.1 História do Surgimento
A humanidade frequentemente chega à ideia de construir um novo projeto grandioso, que ofuscará todos os projetos grandiosos anteriores. A pirâmide de Quéops em Gizé — a maior, Burj Khalifa em Dubai — a mais alta, e a Grande Muralha da China — a mais longa.
No entanto, organizar o trabalho nesses projetos é bem complicado. Se você fosse construir uma nova pirâmide duas vezes mais alta, isso exigiria oito vezes mais pedras. Então, seria necessário ou aumentar a produtividade das pedreiras, ou abrir mais minas.
Também seria necessário encontrar oito vezes mais engenheiros, possivelmente abrir escolas de engenharia, padronizar desenhos, geometria, escrita. Em suma, um desafio e tanto...
Nos milênios que se passaram desde a construção das pirâmides, nada mudou — as pessoas ainda pensam em como fazer cada vez mais trabalho em menos tempo. E quando os computadores foram inventados, as melhores mentes da humanidade começaram a trabalhar nessa questão.
Como executar um programa dez vezes mais rápido? Parece uma pergunta estranha — se o processador já está operando na velocidade máxima, então, talvez, de jeito nenhum. No entanto, eu mencionei as melhores mentes da humanidade por um motivo. Eles analisaram o trabalho de todos os programas e concluíram que há três grandes direções para crescimento.
Eliminação de Ociosidade
Acontece que a maior parte do tempo o programa fica ocioso. Ele está constantemente esperando por algo: os dados precisam ser copiados de um lugar da memória para outro, carregados do disco rígido para a memória, esperando a resposta do servidor para uma requisição, entrada de dados do usuário etc.
Todas essas tarefas não são executadas pelo processador central, mas sim pelos controladores de memória, disco etc. E o processador central durante esse tempo poderia ser carregado com algo útil. Assim surgiu a ideia de rodar em um único processador não apenas uma thread de execução de comandos, mas várias.
E enquanto, por exemplo, uma thread espera pela entrada de dados do usuário, a segunda baixa algo pela rede, a terceira processa dados, a quarta desenha imagens na tela. Depois, essa tarefa evolui para tarefas assíncronas e coroutines, mas falaremos disso mais adiante.
Mais Programas
Se os programas ficam ociosos 80% do tempo, isso obviamente não é bom. Por outro lado, não dá para reescrever todos os programas para nossos novos métodos. Talvez, esse problema possa ser resolvido de outra forma — simplesmente rodando vários programas no computador ao mesmo tempo.
Nesse caso, o sistema operacional monitoria o trabalho dos programas, e se um programa está ocioso, ele passa seu tempo de execução para outro programa. Essa troca acontece dezenas de vezes por segundo, e o usuário simplesmente não percebe as trocas — do seu ponto de vista, os programas são executados simultaneamente.
Mais Processadores
A execução simultânea de programas é incrível, mas e se houver muitos programas, e eles ficarem pouco ociosos? Sem ociosidade — sem uso eficaz. Por exemplo, temos dez programas que estão calculando algo, ou um jogo que consome muitos recursos e algo mais junto com ele.
A solução para este problema foi a adição no processador de vários processadores. Para evitar confusão, passaram a chamá-los de núcleos. Agora temos processadores com vários núcleos (subprocessadores), nos quais dezenas de programas são executados paralelamente.
Interessante! Número de núcleos em processadores
Atualmente, processadores de servidor podem ter de 64 a 256 núcleos, e em alguns casos especializados ainda mais. Por exemplo, os processadores AMD EPYC de 4ª geração oferecem até 96 núcleos, e o IBM POWER10 pode ter até 240 núcleos por chip. Processadores de usuário também evoluíram significativamente: CPUs de desktop de alto desempenho, como o AMD Threadripper, podem ter até 64 núcleos, enquanto modelos mais comuns geralmente têm de 6 a 16 núcleos.
Onde isso vai parar? Tem muito por vir! Primeiro, em uma placa-mãe de servidor você pode instalar vários processadores, por exemplo, dois ou até quatro. Em segundo, servidores podem ser agrupados em racks de servidor com 10-20 cada. E racks de servidor — em data centers, onde existem milhares desses racks.
Sistemas distribuídos e arquiteturas de microsserviços tornaram-se práticas comuns no desenvolvimento moderno de software. Muitas grandes empresas, como Google, Amazon, Netflix, e até mesmo empresas menores, usam sistemas distribuídos para processar grandes volumes de dados, garantir alta disponibilidade e escalabilidade de seus serviços. Esses sistemas permitem o uso eficaz dos recursos de muitos servidores trabalhando juntos como uma unidade, o que aumenta significativamente o desempenho e a resiliência de aplicações.
1.2 Vantagens
Multithreading, ou execução simultânea de tarefas dentro de um programa, oferece várias vantagens-chave que podem melhorar significativamente o desempenho e a eficiência do software. Vamos considerar as principais vantagens do multithreading.
1. Aumento de desempenho e velocidade de execução
Execução paralela de tarefas: Multithreading permite que várias tarefas sejam executadas simultaneamente, o que é especialmente útil para programas que exigem a execução de muitas operações independentes. Isso permite acelerar a execução do programa, já que as tarefas são executadas em paralelo, utilizando todos os recursos disponíveis do processador.
Uso de processadores multi-core: Processadores modernos têm vários núcleos, e o multithreading permite usar totalmente seu poder, distribuindo threads por diferentes núcleos para executar tarefas em paralelo.
2. Melhoria da responsividade e interação com o usuário
Tarefas em background: Multithreading permite que operações longas ou que consomem muitos recursos sejam executadas em segundo plano, mantendo o thread principal, que cuida da interface do usuário, responsivo. Isso melhora a experiência do usuário, pois a interface não fica bloqueada enquanto tarefas pesadas são executadas.
Operações assíncronas: A interação com o usuário e processamento de eventos podem ocorrer em paralelo com a execução das principais tarefas, tornando as aplicações mais responsivas e eficientes.
3. Uso eficaz dos recursos do sistema
Otimização de recursos: Multithreading permite usar mais eficazmente os recursos do sistema, como tempo do processador e memória. Isso é especialmente importante para servidores e sistemas de computação de alto desempenho, onde o multithreading permite processar um grande número de requisições e tarefas simultaneamente.
Gerenciamento de entrada e saída: Aplicativos multithread podem gerenciar tarefas de entrada e saída (por exemplo, operações de rede, leitura e escrita de arquivos) mais eficazmente, já que threads podem executar outras tarefas enquanto uma delas aguarda a conclusão de uma operação de entrada e saída.
4. Suporte à multitarefa
Multitarefa: Multithreading permite executar várias tarefas ao mesmo tempo em um único processo, o que facilita o desenvolvimento de aplicações que requerem multitarefa, como servidores web, bancos de dados e aplicações em tempo real.
Processamento paralelo de dados: Em tarefas relacionadas ao processamento de grandes volumes de dados, multithreading permite dividir os dados em partes e processá-los em paralelo, o que acelera significativamente a execução da tarefa.
Exemplos de uso de multithreading
Servidores web: Multithreading permite que servidores web processem múltiplas requisições de clientes simultaneamente, aumentando o desempenho e escalabilidade dos servidores.
Interfaces gráficas de usuário (GUI): Em aplicações com interface gráfica, multithreading permite que cálculos longos sejam executados em segundo plano, mantendo a interface responsiva.
Cálculos científicos: Multithreading é usado para o processamento paralelo de dados em cálculos científicos, o que permite acelerar significativamente a execução de tarefas computacionais complexas.
Jogos e simulações: Em jogos, multithreading permite processar simultaneamente física, gráficos, sons e ações do usuário, melhorando o desempenho e realismo.
1.3 Nome Correto
Há algumas reclamações sobre o nome "multithreading". Ele é composto por duas palavras: "multi" e "thread", como se sugerisse que dentro do programa há muitas "threads de execução de comandos" fazendo algo.
Uma analogia bonita, mas na literatura em inglês (original) para denotar
várias ações sendo executadas em paralelo, o termo usado é
"thread" (thread
). E, portanto, multithreading lá soa como
multi-threading
.
Isso poderia ser considerado um pequeno mal-entendido — quem se importa como
diferentes termos são traduzidos para outro idioma, se em programação não
começaram a usar ativamente uma coisa chamada
stream
, que além de "fluxo"
não pode ser traduzida.
Portanto, na terminologia de língua portuguesa existe alguma confusão, que é resolvida de duas maneiras:
-
Thread
(thread) é traduzido como "fluxo de execução [de comandos]". Stream
(fluxo) é traduzido como "fluxo de dados".
Por outro lado, muitos programadores simplesmente começaram a usar termos em inglês sem tradução:
-
Thread
(thread) é pronunciado como "thread", e multi-threading como "multi-threading". Stream
(fluxo) é pronunciado como "stream".
Thread é frequentemente chamada de "thread", mas o termo "muitas threads" não pegou. Portanto, muitas vezes no discurso se usa "thread" e "multithreading" juntos.
O uso de muitos termos emprestados enriquece o idioma, permite dar novos significados às palavras e facilita a comunicação com colegas de outros países. Eu sou totalmente a favor dessa abordagem.
GO TO FULL VERSION