Threads e processos
Todo programa em execução é um processo com seu próprio espaço de memória. Threads são "linhas de execução" dentro do mesmo processo — compartilham código, heap e dados globais, mas cada uma tem sua própria stack e registradores. Essa memória compartilhada é ao mesmo tempo a maior vantagem e o maior risco das threads.
Processo = apartamento. Thread = morador.
Um processo é como um apartamento: tem seu próprio endereço, móveis (heap) e não pode acessar diretamente o apartamento do vizinho. Uma thread é como um morador dentro desse apartamento — pode usar todos os cômodos (memória compartilhada), mas se dois moradores tentarem modificar a mesma coisa ao mesmo tempo, causam conflito.
Processos têm isolamento mas comunicação cara (IPC). Threads têm comunicação barata (memória compartilhada) mas exigem sincronização.
Modelo de memória: processo com múltiplas threads
Selecione o número de threads. Veja como elas compartilham código, dados e heap, mas cada uma tem sua stack privada.
Criando threads em Python
# Python: criando threads com threading.Thread import threading counter = 0 # variável global compartilhada — PERIGOSO! def incrementar(): global counter for _ in range(1_000_000): counter += 1 # NÃO é atômico → race condition t1 = threading.Thread(target=incrementar) t2 = threading.Thread(target=incrementar) t1.start(); t2.start() t1.join(); t2.join() print(counter) # provavelmente < 2.000.000 — race condition! # Processo vs Thread: # Processo: espaço de endereçamento próprio, isolado # Thread: execução dentro de um processo, compartilha memória # fork() cria processo filho (cópia do pai) # pthread_create() / Thread() cria thread no mesmo processo
Processo vs Thread — fork() vs pthread_create()
// C: diferença entre processo e thread na prática // Criar processo (Unix) — espaço de endereçamento separado pid_t pid = fork(); if (pid == 0) { // Processo filho: cópia independente do pai // Modificar variável aqui NÃO afeta o pai (COW) } // Criar thread (POSIX) — compartilha memória com o criador pthread_t t; pthread_create(&t, NULL, thread_func, NULL); // thread_func rodando em paralelo agora // Acesso a variáveis globais/heap é compartilhado → sincronize! pthread_join(t, NULL); // espera thread terminar // Python: GIL (Global Interpreter Lock) // O CPython tem um lock global que impede que 2 threads Python // rodem bytecode simultaneamente. Threads são úteis para I/O // mas NÃO para CPU-bound paralelo. Use multiprocessing para isso.
multiprocessing (processos separados) ou C extensions
que liberam o GIL.
Explorando threads na prática
Mini projeto: reproduza a race condition do contador em Python: crie 2 threads que cada uma incrementa uma variável global 1.000.000 vezes. Execute várias vezes e observe valores diferentes. Depois use threading.Lock para corrigir — e verifique que o resultado é sempre 2.000.000.
Projeto principal: implemente um web scraper concorrente que busca o título HTML de 20 URLs em paralelo usando threading.Thread. Compare o tempo com a versão sequencial. Meça também com concurrent.futures.ThreadPoolExecutor. Por que threads ajudam aqui mesmo com o GIL?
Desafio extra: em C, crie um programa com 4 threads que calculam a soma de partes diferentes de um array de 100 milhões de inteiros (paralelismo de dados). Use pthread_create e pthread_join. Compare com a versão single-thread. Calcule o speedup real e compare com o previsto pela Lei de Amdahl (lição 9.6).
Teste sua intuição
Onde você encontra isso
Servidores web — thread per request
Apache (modo prefork) cria um processo por requisição; Apache MPM worker e Nginx usam threads. Node.js usa um único thread com event loop. Go usa goroutines (threads ultra-leves, ~2KB de stack). Cada modelo tem tradeoffs: thread-per-request é simples mas usa muita memória; event loop é eficiente para I/O mas bloqueia no CPU.
Jogos — threads por subsistema
Engines de jogos modernos (Unreal, Unity, Godot) usam múltiplas threads: thread de renderização, thread de física, thread de áudio, thread de rede. Cada subsistema roda independentemente. A comunicação entre threads é feita com filas de mensagens para evitar race conditions. O Unreal Engine 5 usa um sistema de "task graph" com centenas de tarefas paralelas por frame.
Bancos de dados — isolamento de transações
PostgreSQL cria um processo por conexão (não thread) para isolamento total. MySQL usa threads. SQLite permite apenas uma thread escrevendo por vez (WAL mode melhora isso). Sistemas como Spanner e CockroachDB usam concorrência massiva com protocolos de consenso (Paxos, Raft) para garantir consistência entre servidores.