8.7. Eventos¶
asyncio.Event é a primitiva de sinalização mais simples que o módulo fornece. Uma coroutine define um evento quando algo aconteceu; qualquer número de outras coroutines aguarda que o evento seja definido antes de continuar. Não há carga útil – um evento é um booleano que passa a ativo e permanece assim até que algo o limpe.
8.7.1. A forma básica¶
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 de espera corre, atinge await evt.wait(), e é suspensa – o evento começa no estado limpo, por isso a chamada wait cede de volta ao ciclo. Um segundo depois main chama evt.set() e a tarefa de espera é agendada para retomar. O await asyncio.sleep(0) final é apenas uma cedência para que o ciclo tenha oportunidade de correr a tarefa de espera antes de main retornar.
8.7.2. Os quatro métodos¶
set()– define o evento e agenda todas as coroutines atualmente bloqueadas emwait()para retomar. O evento permanece definido até ser limpo.clear()– repõe o evento para o estado limpo. As coroutines que chamaremawait wait()depois disso bloquearão novamente.wait()– uma coroutine. Se o evento já estiver definido, retorna imediatamente. Caso contrário, bloqueia até algo o definir.is_set()– devolve o estado atual sem bloquear. Útil quando uma coroutine quer verificar o evento sem esperar por ele.
8.7.3. Múltiplos aguardadores¶
Várias coroutines podem fazer await no mesmo evento. Quando algo chama set(), todas são agendadas para retomar. O evento é por defeito de um-para-muitos; não é necessário ter vários eventos para encaminhar o sinal para vários aguardadores.
8.7.4. Reutilização de um evento¶
Um padrão comum é usar um evento repetidamente – uma coroutine que corre 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 limpa-o depois de ter recebido o sinal. Se o produtor puder definir o evento novamente antes de o trabalhador ter acordado, o trabalhador pode querer tratar esse caso (while go.is_set(): ...) antes de limpar.
8.7.5. Contextos seguros¶
Event não é seguro para chamar set() de dentro de um handler de interrupção. O mecanismo que usa para agendar os aguardadores assume que está a correr dentro do contexto do próprio ciclo de eventos, o que as interrupções não satisfazem. Para acordar uma tarefa asyncio a partir de um handler de interrupção, ThreadSafeFlag (abordado em breve) é a primitiva correta.