Asyncio Avançado: Programação Assíncrona Eficiente em Python

asyncio é o módulo nativo do Python para programação assíncrona, permitindo a execução de tarefas concorrentes sem a necessidade de múltiplas threads ou processos, ideal para operações de I/O intensivo, como requisições de rede, leitura/escrita de arquivos e pipelines de dados.


1. Conceitos Fundamentais

1.1 Eventos e Loop

import asyncio

async def tarefa():
    print("Início")
    await asyncio.sleep(1)
    print("Fim")

asyncio.run(tarefa())
  • async def define uma coroutine, que só é executada quando await é chamado.

  • await pausa a coroutine sem bloquear o event loop, permitindo que outras tarefas rodem.


1.2 Corroutines vs Tasks

  • Coroutine: função assíncrona, executada pelo event loop.

  • Task: coroutine agendada para execução concurrentemente.

async def main():
    t1 = asyncio.create_task(tarefa())
    t2 = asyncio.create_task(tarefa())
    await t1
    await t2

asyncio.run(main())
  • Tasks permitem executar múltiplas coroutines simultaneamente.


2. Concorrência Avançada

2.1 asyncio.gather

  • Executa múltiplas coroutines concorrentemente e retorna resultados:

results = await asyncio.gather(tarefa(), tarefa())
  • Mantém ordem dos resultados, mesmo que uma coroutine termine antes da outra.

2.2 asyncio.wait

  • Permite esperar por múltiplas tarefas com controle de timeout ou retorno parcial:

done, pending = await asyncio.wait([t1, t2], timeout=1)
  • Útil em pipelines onde algumas tarefas podem expirar.


2.3 Controle de Concurrency

  • Limita o número de coroutines simultâneas com Semáforos:

sem = asyncio.Semaphore(3)

async def worker(n):
    async with sem:
        await asyncio.sleep(1)
        print(f"Tarefa {n} concluída")

tasks = [worker(i) for i in range(10)]
await asyncio.gather(*tasks)
  • Evita sobrecarregar recursos externos (APIs, banco de dados, rede).


3. Asyncio e I/O Avançado

3.1 Requisições HTTP assíncronas

  • Com aiohttp, podemos realizar múltiplas requisições em paralelo:

import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.text()

urls = ["https://example.com" for _ in range(5)]
results = await asyncio.gather(*(fetch(url) for url in urls))

3.2 Leitura e escrita assíncrona de arquivos

import aiofiles

async def escreve_arquivo(nome, texto):
    async with aiofiles.open(nome, mode="w") as f:
        await f.write(texto)

await escreve_arquivo("saida.txt", "Conteúdo assíncrono")
  • Permite manipulação de arquivos sem bloquear I/O, útil em pipelines de dados massivos.


4. Padrões de Design Assíncronos

4.1 Produtor-Consumidor

import asyncio
import random

fila = asyncio.Queue()

async def produtor():
    for i in range(10):
        await fila.put(i)
        print(f"Produziu {i}")
        await asyncio.sleep(random.random())

async def consumidor():
    while True:
        item = await fila.get()
        print(f"Consumiu {item}")
        fila.task_done()
        if item == 9: break

await asyncio.gather(produtor(), consumidor())

4.2 Timeout e Cancelamento

async def tarefa_lenta():
    await asyncio.sleep(10)

try:
    await asyncio.wait_for(tarefa_lenta(), timeout=2)
except asyncio.TimeoutError:
    print("Timeout ocorrido")
  • Garante controle sobre tarefas longas e evita travamento do loop.


5. Estratégias Avançadas

5.1 Combinar Tasks e Gather com timeout

tasks = [asyncio.create_task(fetch(url)) for url in urls]
done, pending = await asyncio.wait(tasks, timeout=5)
  • Permite coletar resultados parciais e cancelar tasks pendentes.

5.2 Limitar concorrência global

  • Combinar Semaphore com pipeline de milhares de coroutines:

sem = asyncio.Semaphore(10)

async def limitada(url):
    async with sem:
        return await fetch(url)
  • Evita sobrecarga em APIs externas ou banco de dados.

5.3 Loop customizado

  • Para cenários avançados, podemos controlar o event loop manualmente:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
tasks = [loop.create_task(fetch(url)) for url in urls]
loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
  • Útil em integração com frameworks externos.


6. Boas Práticas Profissionais

  1. Prefira async/await em vez de callbacks para clareza e manutenção.

  2. Use aiohttp, aiofiles ou bibliotecas que liberam I/O para máxima performance.

  3. Combine Semaphore e Queue para limitar e organizar concorrência.

  4. Monitore timeouts e cancelamentos, evitando bloqueio do loop.

  5. Evite misturar threads e asyncio, prefira asyncio puro ou multiprocessing para CPU-bound.

  6. Documente claramente fluxo assíncrono e pontos de concorrência.


7. Aplicações Profissionais

  • Web scraping massivo assíncrono

  • Integração com APIs externas de forma eficiente

  • ETL e pipelines de dados assíncronos

  • Monitoramento e coleta de dados em tempo real

  • Serviços de chat, websockets e microservices assíncronos


8. Conclusão

  • Asyncio permite programação concorrente eficiente sem múltiplas threads.

  • Ferramentas avançadas incluem gather, wait, Semaphore, Queue, timeout e cancelamento.

  • Combinando com aiohttp, aiofiles e design de pipelines, é possível criar aplicações escaláveis e responsivas.

  • Ideal para I/O-bound massivo, pipelines de dados e microservices modernos.

Comentários

Postagens mais visitadas deste blog

Gerando Relatórios em PDF com Python (ReportLab e FPDF)

Manipulação de Arquivos no C#: Como Ler, Escrever e Trabalhar com Arquivos de Forma Simples

Laços de Repetição em Python: Conceitos e Exemplos Práticos