import asyncio from random import randint import time async def funcao_assincrona_com_possivel_chamada_sincrona(n: int): # A corrotina é uma função educadinha, que quando vai fazer # algo que pode demorar fala assim: # "Cara, isso aqui vai demorar pois é operação de IO, podem ir fazendo outra coisa" # Assin o interpretador é liberado para executar outras instruções. for i in range(10): print(f'Task {n} - {i}') # Neste ponto (await) a corrotina concede o direito de execução # de volta, ai é permitido rodar outras corrotinas # que estiverem aguardando, é aqui que a rotina fala # "Gente, pode continuar ai que vou demorar aqui um pouco" # Aqui poderia ser uma requisição web demorada, # um acesso a bancos de dados e outras coisas, por exemplo. print(f'Liberando task {n} - {i}') await asyncio.sleep(randint(0,3)) print(f'Retomando task {n} - {i}') # Aqui acontece a quebra, existe a chance (1 em 1000) de # ser executado um processo blocante que irá # blocar todos as tarefas que estão rodando concorrentemente # Ela não é tão educada quanto parecia quando entra aqui. sorte = randint(0, 1000) if sorte == 50: print print(f'Task {n} blocou a GIL no passo {i}, que azar') time.sleep(3) # Isso aqui blocante! print(f'Concluindo task {n} - {i}') async def main(): # Irá criar mil tarefinhas que serão disparadas em "paralelo" (na verdade concorrentemente) tasks_assincronas_que_podem_blocar = { asyncio.create_task( funcao_assincrona_com_coisa_assincrona(i) ) for i in range(10000) } # Executa todas elas, aguardando todas serem concluidas await asyncio.wait( tasks_assincronas_que_podem_blocar, return_when=asyncio.ALL_COMPLETED ) if __name__ == "__main__": asyncio.run(main())