Como Funciona o Algoritmo de Threads: O Guia Completo para Entender Concorrência, Paralelismo e Boas Práticas
Como funciona o algoritmo de threads
O que é uma thread (e por que ela existe)
- Aplicações com interface: uma thread cuida da interface e outra realiza operações demoradas.
- Servidores: cada requisição pode ser atendida por uma thread (ou por um modelo híbrido).
- Programas com tarefas em paralelo: processamento de dados em “fatias”.
Então… o que é “algoritmo de threads”?
- Decidir qual thread deve rodar no processador (escalonamento).
- Interromper e retomar threads conforme necessário (preempção).
- Sincronizar o acesso a dados compartilhados (mecanismos de lock, semáforos, monitores, etc.).
Estados das threads: o “roteiro” da execução
- New (Nova): thread acabou de ser criada.
- Ready (Pronta): thread está pronta para rodar, esperando sua vez.
- Running (Executando): thread está de fato rodando no CPU.
- Waiting/Blocked (Bloqueada): thread aguardando algo (I/O, lock, semáforo, evento).
- Terminated (Encerrada): execução finalizada.
Escalonamento: quem roda primeiro?
1) Round Robin (Rodízio)
2) Prioridades
3) Múltiplas filas (Multi-Level Feedback Queue)
Preempção: a CPU pode interromper?
Preemptivo
Não preemptivo
Concorrência vs. Paralelismo (e como threads entram nisso)
- Concorrência: várias tarefas fazem progresso “ao mesmo tempo”, mesmo que um único núcleo esteja presente (pela alternância rápida).
- Paralelismo: várias tarefas rodam de fato simultaneamente (em múltiplos núcleos/CPUs).
Sincronização: o ponto onde bugs nascem
Condição de corrida (race condition)
contador. Duas threads leem o valor atual, incrementam e escrevem de volta. Se ambas fizerem isso ao mesmo tempo, você pode perder incrementos. O resultado final depende da ordem “acidental” de execução.Locks (mutex)
Semáforos
Condições (condition variables)
Deadlock: quando tudo para (e por que isso acontece)
- Thread A pega Lock1 e tenta pegar Lock2.
- Thread B pega Lock2 e tenta pegar Lock1.
- Definir uma ordem global de aquisição de locks.
- Usar lock com timeout e fallback.
- Reduzir a granularidade dos locks (evitar lock excessivo).
Starvation: quando uma thread “não consegue vez”
Cache, afinidade e a vida real do desempenho
- Cache do processador: trocar de thread pode invalidar caches e custar caro.
- Affinity: alguns sistemas tentam manter uma thread no mesmo núcleo para melhorar cache locality.
- Overhead de troca de contexto: alternar entre threads tem custo (salvar/restaurar estado).
Exemplo mental: o “algoritmo” como um mestre de cerimônias
- Escolhe quem toca agora (escalonamento).
- Interrompe quando o tempo do músico termina (preempção/quantum).
- Faz quem precisa de um instrumento esperar (bloqueio por recursos).
- Garante que ninguém use o mesmo instrumento ao mesmo tempo (sincronização).
Threads em linguagens populares: como o runtime entra na história
- Java: JVM gerencia threads e pode usar pools (ExecutorService) para reduzir overhead.
- C#: Task Parallel Library (TPL) e async/await mudam o modelo de execução, embora ainda exista escalonamento.
- Python: CPython tem GIL (Global Interpreter Lock), que altera o comportamento de CPU-bound em threads.
- C/C++: std::thread e bibliotecas de sincronização dependem diretamente do comportamento do SO.
Boas práticas para programar com threads
- Minimize compartilhamento: prefira estruturas imutáveis ou “dados por thread”.
- Use locks com parcimônia: muito lock vira gargalo.
- Defina ordem de locks: reduz chance de deadlock.
- Prefira pools: ao invés de criar threads a cada tarefa, reutilize (ThreadPool).
- Meça antes: “otimização” sem benchmark pode piorar desempenho.
- Teste sob carga: condições de corrida aparecem melhor com estresse e repetição.
Threads vs. alternativas modernas: async, event loop e filas
- Async/await: útil para I/O (rede, disco) porque você não “bloqueia” thread esperando.
- Event loop: modelo orientado a eventos, comum em Node.js e frameworks semelhantes.
- Filas de mensagens: threads/processos comunicam por mensagens em vez de compartilhar memória diretamente.
- Processos em vez de threads: isolam memória, reduzindo certas classes de bugs.






