8.9. ThreadSafeFlag¶
asyncio.ThreadSafeFlag é a primitiva de sinalização que o asyncio fornece para o caso que Event não suporta: um handler de interrupção precisa de acordar uma tarefa asyncio. A interrupção dispara fora do agendamento normal do ciclo de eventos, pelo que a manutenção de registos que asyncio.Event.set() faz não pode ser feita de forma segura a partir dele.
8.9.1. Porquê uma primitiva separada¶
asyncio.Event.set() executa a manutenção de registos que acorda as coroutines em espera partindo do pressuposto de que está a ser chamado de dentro de uma tarefa que o ciclo está atualmente a correr. Um handler de interrupção não satisfaz esse pressuposto – pode disparar entre quaisquer duas instruções e corromper o que quer que o ciclo estivesse a fazer a meio.
asyncio.ThreadSafeFlag.set() está escrito para ser seguro nesses contextos. A compensação por essa segurança é um conjunto de funcionalidades mais reduzido do que Event: um ThreadSafeFlag só pode ser aguardado por uma tarefa de cada vez, e a flag é automaticamente reposta quando a tarefa em espera acorda.
8.9.2. A forma básica¶
Uma utilização típica é um handler de interrupção GPIO a transferir um evento de pressão de botão para uma tarefa asyncio:
import asyncio
from machine import Pin
flag = asyncio.ThreadSafeFlag()
def on_press(pin):
flag.set()
async def watcher():
button = Pin("P1", Pin.IN, Pin.PULL_UP)
button.irq(handler=on_press, trigger=Pin.IRQ_FALLING)
while True:
await flag.wait()
print("button pressed")
asyncio.run(watcher())
on_press corre em contexto de interrupção sempre que o GPIO dispara; tudo o que faz é chamar flag.set(). A tarefa asyncio watcher faz awaitem flag.wait() num ciclo; cada vez que a flag é definida, a tarefa retoma, executa o trabalho que a pressão do botão requer, e volta ao ciclo de espera.
8.9.3. Os três métodos¶
set()– marca a flag como definida. Se uma tarefa estiver atualmente em espera emwait(), agenda-a para retomar. Seguro para chamar a partir de um handler de interrupção.wait()– uma coroutine. Bloqueia até a flag estar definida, depois retorna. A flag é automaticamente limpa no retorno – a próxima chamada awaitbloqueará novamente até ao próximoset().clear()– limpa explicitamente a flag sem esperar por ela. Útil antes de um primeirowaitpara descartar quaisquerset()espúrios que tenham disparado antes de a tarefa estar pronta.
8.9.4. Apenas um aguardador¶
Apenas uma tarefa pode estar em wait() de cada vez. A forma que uma aplicação quer é uma única tarefa proprietária da flag – geralmente a mesma tarefa que instalou o handler de interrupção – com outras tarefas a comunicar com ela através de Event, Lock, ou estruturas de dados partilhadas protegidas por essas primitivas.
Quando um botão tem de acordar várias tarefas, a tarefa proprietária é o local para fazer o encaminhamento: o observador acima poderia chamar some_event.set() após cada pressão, e todas as tarefas em espera em some_event retomavam.