Introdução ao multithreading e multiprocessing em Python
Ao desenvolver aplicações modernas, muitas vezes é necessário aumentar o desempenho. Isso é alcançado através da execução paralela de tarefas. Em Python, ferramentas como multithreading e multiprocessing são usadas para isso. Apesar da Global Interpreter Lock (GIL), o multithreading em Python pode acelerar significativamente os programas. Isso é especialmente relevante para tarefas de I/O (entrada/saída), como requisições de rede ou manipulação de arquivos.
Neste guia, vamos explorar como o multithreading funciona em Python. Abordaremos a biblioteca threading, compararemos com multiprocessing e daremos exemplos práticos. Isso ajudará você a otimizar seu código para uma execução mais rápida.
O que é multithreading em Python
Multithreading em Python permite executar várias threads (linhas de execução) dentro de um único processo. Cada thread executa uma parte do código em paralelo. Essa abordagem é eficiente para tarefas que envolvem espera por recursos externos.
Vantagens do multithreading para tarefas de I/O
O multithreading acelera programas quando as tarefas incluem:
- Requisições de rede, como baixar dados da internet.
- Manipulação de arquivos, incluindo leitura e escrita.
- Acesso a bancos de dados.
- Outras operações de I/O onde a CPU fica ociosa aguardando.
No entanto, o multithreading tem limitações. Em Python, existe a Global Interpreter Lock (GIL). A GIL impede a execução simultânea de bytecode Python em múltiplas threads. Por isso, o multithreading não é adequado para tarefas com uso intensivo de CPU, que exigem processamento pesado.
Quando usar multithreading ou multiprocessing
A escolha entre multithreading e multiprocessing depende do tipo de tarefa. Multithreading é ideal para cenários com espera, enquanto multiprocessing é melhor para cálculos.
Recomendações de cenários
- Tarefas com alto volume de I/O: Use threading para evitar bloquear a thread principal.
- Tarefas computacionalmente intensivas: Utilize multiprocessing para contornar a GIL e aproveitar múltiplos núcleos da CPU.
- Trabalho com APIs de rede: Escolha threading para processar requisições de forma eficiente.
- Processamento paralelo de dados: Multiprocessing é ideal para distribuir a carga entre processadores.
Essa abordagem ajuda a evitar o uso ineficiente de recursos e acelera a execução do programa.
Biblioteca threading: fundamentos e exemplos
A biblioteca threading em Python fornece ferramentas para criar e gerenciar threads. Ela é simples de usar e adequada para iniciantes.
Exemplo simples de criação de thread
Aqui está um exemplo básico de multithreading em Python:
import threading
import time
def print_numbers():
for i in range(5):
print(f"Número: {i}")
time.sleep(1)
# Criando uma thread
thread = threading.Thread(target=print_numbers)
thread.start()
# A thread principal continua
print("Thread principal finalizada!")
Neste código, a thread principal termina imediatamente, enquanto a função print_numbers é executada em uma thread separada. Isso demonstra como o multithreading permite continuar o trabalho sem esperar.
Passando argumentos para a thread
Para passar dados para a função da thread, use o parâmetro args:
def greet(name):
print(f"Olá, {name}!")
thread = threading.Thread(target=greet, args=("João",))
thread.start()
Aqui, o argumento "João" é passado para a função greet. Isso é útil para parametrizar tarefas nas threads.
Aguardando a conclusão das threads com join
O método join permite aguardar a conclusão de uma thread:
import threading
import time
def task():
time.sleep(2)
print("Tarefa concluída!")
thread = threading.Thread(target=task)
thread.start()
thread.join() # Aguarda a thread terminar
print("Thread principal continuou após a tarefa.")
Isso garante que a thread principal só prossiga após a conclusão da thread filha, útil para coordenar a execução.