Multithreading em Python

0 – Python
4 – Paralela e Concorrente
4.1 – Multithreading (threading)
4.2 – Multiprocessamento (multiprocessing)
4.3 – Assíncrona (asyncio)
4.4 – async ou await
4.4 – Distribuida
LEGENDA
Nivel_1
Nivel_2
Nivel_3

Multithreading permite múltiplas threads dentro de um único processo. Cada thread executa um fluxo independente de instruções. Primeiramente, threads compartilham a mesma memória do processo. Por exemplo, duas threads podem acessar a mesma variável global. Isso facilita a comunicação entre elas. Além disso, threads são mais leves que processos completos. A voz passiva é usada aqui: “as threads são gerenciadas pelo sistema operacional”. Quando utilizar multithreading em Python? Em operações de I/O. Por exemplo, downloads de rede, leitura de arquivos ou consultas a banco. No entanto, o GIL (Global Interpreter Lock) limita a execução paralela. Portanto, threads não aceleram código com CPU intensivo. Vamos explorar criação, sincronização e boas práticas. Três subtítulos guiarão você pelo mundo do threading. Ao final, você dominará a concorrência em Python.

Criando e gerenciando threads

Python oferece o módulo threading para trabalhar com threads. A classe Thread representa uma thread executável. Para criar uma thread, instancie Thread(target=funcao). Depois, chame start() para iniciar a execução. O método join() espera a thread terminar. Quando usar criação manual? Em scripts com poucas threads controladas. A voz passiva é aplicada: “os argumentos são passados via args“. Exemplo básico de criação de threads:

Threads executam concorrentemente, não em paralelo real. O GIL alterna entre elas rapidamente, dando a ilusão de simultaneidade. Para I/O, isso é suficiente e muito eficiente.

Sincronização entre threads

Quando threads compartilham dados, podem ocorrer condições de corrida. Por exemplo, duas threads incrementando a mesma variável simultaneamente. Isso corrompe o resultado final. Para evitar isso, use mecanismos de sincronização. O Lock (mutex) permite que apenas uma thread execute uma seção crítica. Outros mecanismos incluem RLock, Semaphore e Event. Quando usar locks? Sempre que múltiplas threads acessarem dados compartilhados. A voz passiva é usada aqui: “os recursos compartilhados são protegidos por locks”. Exemplo de contador seguro com lock:

O resultado sem lock será imprevisível e geralmente menor que 500000. Com lock, o resultado é sempre correto, porém mais lento. A fórmula do tempo de execução com contenção é: \(T = T_0 + C \times L\) Onde C é o número de aquisições e L é a latência do lock.

Threads vs. outras formas de concorrência

Threads são ótimas para I/O-bound com poucas conexões. Para CPU-bound, prefira multiprocessing (processos separados). Para milhares de conexões simultâneas, asyncio é superior. Quando escolher threading especificamente? Em projetos simples. Também quando você já tem código síncrono e não quer reescrever. Threads são mais fáceis de entender que asyncio. A desvantagem é o GIL e a complexidade de locks. A voz passiva é aplicada: “decisões de arquitetura são baseadas no tipo de tarefa”. Exemplo prático de pool de threads para tarefas I/O:

ThreadPoolExecutor simplifica o gerenciamento de threads. Ele reutiliza threads para evitar custo de criação. Use max_workers entre 5 e 10 para tarefas de rede. Nunca crie mais de 100 threads simultâneas. O sistema operacional pode ficar sobrecarregado. Threads são ferramentas poderosas quando usadas corretamente. Domine os locks e evite deadlocks. Sua aplicação ficará mais rápida e responsiva. Comece com pequenos exemplos e escale gradualmente.

Deixe um comentário