8.7. Eventos¶
asyncio.Event é a primitiva de sinalização mais simples que o módulo oferece. Uma corrotina define (sets) um evento quando algo aconteceu; qualquer número de outras corrotinas esperam que o evento seja definido antes de continuar. Não há carga útil – um evento é um booleano que se ativa e permanece ativo até que algo o limpe.
8.7.1. O formato básico¶
import asyncio
async def waiter(evt):
print("waiting")
await evt.wait()
print("got signal")
async def main():
evt = asyncio.Event()
asyncio.create_task(waiter(evt))
await asyncio.sleep(1)
evt.set()
await asyncio.sleep(0)
asyncio.run(main())
A tarefa que espera (waiter) executa, atinge await evt.wait() e é suspensa – o evento começa no estado limpo, então a chamada wait cede o controle de volta ao loop. Um segundo depois, main chama evt.set() e o waiter é agendado para retomar. O await asyncio.sleep(0) ao final é apenas um yield para que o loop tenha a chance de executar o waiter antes que main retorne.
8.7.2. Os quatro métodos¶
set()– ativa o evento e agenda toda corrotina atualmente bloqueada emwait()para retomar. O evento permanece ativo até ser limpo.clear()– retorna o evento ao estado limpo. Corrotinas que fizeremawait wait()depois disso serão bloqueadas novamente.wait()– uma corrotina. Se o evento já estiver ativo, retorna imediatamente. Caso contrário, bloqueia até que algo o ative.is_set()– retorna o estado atual sem bloquear. Útil quando uma corrotina deseja verificar o evento sem esperar por ele.
8.7.3. Múltiplos waiters¶
Várias corrotinas podem fazer await no mesmo evento. Quando algo chama set(), todas são agendadas para retomar. O evento é um-para-muitos por padrão; não há necessidade de vários eventos para distribuir para vários waiters.
8.7.4. Reutilizando um evento¶
Um padrão comum é usar um evento repetidamente – uma corrotina que executa uma vez por sinal – limpando-o após cada passagem:
async def worker(go):
while True:
await go.wait()
do_one_unit_of_work()
go.clear()
O produtor define o evento sempre que há trabalho; o trabalhador o limpa assim que captou o sinal. Se o produtor puder definir o evento novamente antes que o trabalhador tenha acordado, o trabalhador pode querer tratar esse caso (while go.is_set(): ...) antes de limpar.
8.7.5. Contextos seguros¶
Não é seguro chamar set() em Event de dentro de um manipulador de interrupção. O mecanismo que ele usa para agendar os waiters pressupõe que está executando dentro do próprio contexto do event loop, o que as interrupções não são. Para acordar uma tarefa asyncio a partir de um manipulador de interrupção, ThreadSafeFlag (coberto em breve) é a primitiva correta.