Criar milhares de corrotinas não significa que uma API, banco ou pool aceite milhares de operações simultâneas. asyncio.Semaphore mantém um contador de permissões e faz novas tarefas aguardarem quando o limite é atingido.
import asyncio
async def processar(item: int, limite: asyncio.Semaphore) -> int:
async with limite:
await asyncio.sleep(0.1)
return item * 2
async def main() -> None:
limite = asyncio.Semaphore(5)
resultados = await asyncio.gather(
*(processar(item, limite) for item in range(20))
)
print(resultados)
asyncio.run(main())
O bloco async with adquire e libera a permissão de forma segura. Escolha o valor com base no recurso real, como tamanho do pool e limite documentado do serviço, e meça latência, erros e backlog antes de aumentar.
Boas práticas
Semaphore controla concorrência instantânea, não requisições por segundo. Para uma cota temporal, combine uma política de rate limiting adequada. Coloque somente a operação limitada dentro do bloco para não segurar permissões durante trabalho local. Se o valor inicial não puder ser excedido por engano, avalie BoundedSemaphore.
Para organizar tarefas relacionadas, veja asyncio.TaskGroup. Em chamadas HTTP, aplique limites também ao cliente descrito em HTTPX em Python.
A documentação oficial das primitivas de sincronização asyncio, consultada em 22 de julho de 2026, detalha a API e seus limites.