12 pontos por darjeeling 2025-09-02 | 9 comentários | Compartilhar no WhatsApp

Python Async: por que ainda não virou mainstream?

O asyncio do Python é uma ferramenta poderosa que pode reduzir tempos de espera e aumentar drasticamente a eficiência de programas em ambientes com muitas tarefas de I/O (entrada/saída). Ainda assim, apesar das várias vantagens, nem todo desenvolvedor Python o adota ativamente, e há algumas razões fundamentais para isso.


1. Fica tudo mais complexo na cabeça: carga cognitiva

A maior barreira é a complexidade. Código síncrono é intuitivo porque basta acompanhar o fluxo de execução de cima para baixo, como se estivéssemos lendo um livro.

Mas código assíncrono é diferente. É como um cozinheiro que, ao preparar macarrão, deixa a massa cozinhando durante o tempo de espera e, ao mesmo tempo, faz o molho; no tempo que sobra, ainda corta os legumes. No fim, a refeição pode ficar pronta mais rápido, mas, na cabeça do cozinheiro, é preciso acompanhar o tempo todo o estado de várias tarefas: “quanto falta para a massa ficar pronta?”, “o molho vai queimar?”.

Como o fluxo de execução do código fica pulando de um lado para outro, fica difícil entender qual tarefa está rodando agora e qual está esperando. Isso torna o processo de depuração especialmente complicado quando surge um bug, porque rastrear a causa fica muito mais difícil.


2. Um ecossistema fragmentado: compatibilidade de bibliotecas

Outro problema do Python é que o ecossistema de bibliotecas está dividido entre “síncrono” e “assíncrono”. Muitas bibliotecas famosas e convenientes (por exemplo, a biblioteca padrão de requisições HTTP requests e muitos ORMs) funcionam apenas de forma síncrona.

Se uma biblioteca síncrona for usada incorretamente dentro de código assíncrono, durante a execução desse código síncrono a principal vantagem da programação assíncrona — o event loop — simplesmente para. É como construir várias faixas numa estrada e usar só uma. Para resolver isso, é preciso aprender e adotar bibliotecas separadas com suporte a async (aiohttp, asyncpg etc.), o que deixa a curva de aprendizado ainda mais íngreme.


3. Só bate em um de cada vez: GIL (Global Interpreter Lock)

O GIL (Global Interpreter Lock) é uma das características crônicas do Python: dentro de um único processo, mesmo que existam várias threads, apenas uma pode executar por vez. O asyncio roda em uma única thread, então não entra em conflito direto com o GIL, mas a existência do GIL limita o alcance do async.

O asyncio é otimizado para aproveitar tempos de espera de I/O (aguardar resposta de rede, esperar leitura de arquivo etc.). Mas, se uma tarefa intensiva de CPU com cálculo muito pesado estiver dentro de uma função async, todo o event loop para até esse cálculo terminar. Durante esse tempo, outras tarefas de I/O ficam sem poder fazer nada além de esperar. No fim, para trabalhos que exigem paralelismo real, ainda existe a limitação de precisar recorrer a outras técnicas, como multiprocessing.


4. Esperança para o futuro: Python 3.14 e a remoção do GIL

Mas há uma notícia bastante animadora sobre essas limitações: o movimento pela remoção opcional do GIL, introduzido experimentalmente a partir do Python 3.13 e que deve amadurecer ainda mais na versão 3.14.

Essa mudança, impulsionada pela proposta PEP 703, tem como objetivo permitir que desenvolvedores executem código Python sem o GIL, caso desejem. Se isso se concretizar, o Python poderá ter multithreading de verdade, com várias threads aproveitando vários núcleos de CPU ao mesmo tempo.

Isso pode gerar uma sinergia enorme quando usado junto com asyncio. Tarefas de I/O podem continuar sendo tratadas com eficiência pelo asyncio, enquanto tarefas de CPU mais pesadas podem ser enviadas para threads separadas e processadas em paralelo sem a limitação do GIL. Essa mudança tende a ser um grande ponto de virada para o ecossistema Python e há grande expectativa de que derrube muitas das barreiras que vinham dificultando a adoção da programação async.

9 comentários

 
barca105 2025-09-03

Acho que o GIL aparece de forma meio aleatória aí... mesmo que o GIL seja removido,
se a ideia for usar multithread tanto em casos de I/O bound quanto de CPU bound,
talvez seja melhor adotar outra alternativa em vez de Python...

Também parece que asyncio tem uma rejeição bem forte entre quem se aprofunda bastante em Python.
Acho que já ouvi com certa frequência a opinião de que gevent é que deveria ter virado o padrão.

 
sonnet 2025-09-03

Concordo que não dá para esperar que a direção atual do GIL vá se tornar algo que não fique devendo nem quando comparada a “outras alternativas”,
mas dizer que é preciso adotar outras alternativas além do Python não deveria levar a uma linha de argumentação de que não há problema, e sim de que há um problema, não é?

 
jasonroh123 2025-09-02

Usam bastante asyncio... é utilizável... existe a limitação de o cancelamento de tarefas ser edge-triggered (e não level-triggered), mas na prática quase não se escreve código que trate cancelamento de tarefa de forma consciente e elegante; um problema maior é que o event loop mantém referências fracas às tarefas, então elas podem desaparecer por causa do GC... mas isso se resolve com structured concurrency.

Para praticamente qualquer operação principal de I/O, não há dificuldade em encontrar bibliotecas com suporte a asyncio...

E o GIL? Não tem muito a ver com isso... a própria ideia de usar asyncio para paralelizar trabalho CPU-intensive já é meio estranha. Se o GIL melhorar, aí sim isso será útil para multithreading CPU-intensive... async serve para rodar da forma mais eficiente possível os trechos com gargalo de I/O...

Enfim, conclusão: há alguns problemas de design, mas para atingir o objetivo não há grandes dificuldades de uso, e estamos utilizando bem isso em produção.

 
maitrouble 2025-09-05

Você já passou pela experiência de uma tarefa ser coletada pelo GC?

 
sonnet 2025-09-03

Claro, eu também uso .asyncio até cansar em produção, mas não estou tão satisfeito com a experiência de uso atual a ponto de avaliá-la como "estou usando bem"..

 
sonnet 2025-09-03

O asyncio atual foi projetado tendo o GIL como premissa e, de certa forma, como uma estratégia para contorná-lo, então o GIL não interage diretamente com o asyncio.

Mas, olhando da perspectiva de toda a programação concorrente baseada em asyncio, acho que dizer que o GIL não tem relação com isso acaba soando como algo do tipo: "é Python, então é natural que não dê certo."

 
doolayer 2025-09-02

Vou usar o joblib mesmo.

 
sonnet 2025-09-02

O problema do Asyncio não é a dificuldade da programação assíncrona em si, mas a baixa qualidade. Um design que joga fora consistência e universalidade nem é algo tão raro em Python, mas coisas como ProactorEventLoop ainda têm bugs que causam interrupções de serviço e foram reportados há 5 anos.

Para quem é obrigado a usar isso, é bem difícil simplesmente dar risada ao ler um texto desses.

 
sonnet 2025-09-02

Claro, uma razão maior pode ser que, por causa do GIL, o benefício que dá para obter desde o início já é menor do que em outros ambientes.
Acho que dizer que, sem o GIL, seria possível gerar sinergia é algo quase enganoso. Se você colocar uma prótese, ainda que ajude um pouco, em um corredor que não tem uma das pernas, isso é "sinergia"?