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 acordar uma tarefa asyncio. A interrupção dispara fora do escalonamento normal do event loop, então a contabilidade que asyncio.Event.set() faz não pode ser realizada com segurança a partir dele.
8.9.1. Por que uma primitiva separada¶
asyncio.Event.set() executa a contabilidade que acorda as corrotinas em espera partindo da premissa de que está sendo chamada de dentro de uma tarefa que o loop está executando no momento. Um handler de interrupção não satisfaz essa premissa – ele pode disparar entre quaisquer duas instruções e corromper o que quer que o loop estivesse fazendo no meio do caminho.
asyncio.ThreadSafeFlag.set() foi escrito para ser seguro nesses contextos. O custo dessa segurança é um conjunto de recursos menor do que o de Event: uma ThreadSafeFlag só pode ser aguardada por uma tarefa por vez, e a flag é redefinida automaticamente quando a tarefa em espera acorda.
8.9.2. O formato básico¶
Um uso típico é uma interrupção de GPIO repassando um evento de pressionamento 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 é executado em contexto de interrupção sempre que a GPIO dispara; tudo o que ele faz é chamar flag.set(). A tarefa asyncio watcher faz await em flag.wait() dentro de um loop; cada vez que a flag é setada, a tarefa retoma, executa o trabalho que o pressionamento do botão exige e então volta ao loop para aguardar novamente.
8.9.3. Os três métodos¶
set()– marca a flag como setada. Se uma tarefa estiver atualmente aguardando emwait(), agenda sua retomada. Seguro para chamar de um handler de interrupção.wait()– uma corrotina. Bloqueia até que a flag seja setada e então retorna. A flag é limpa automaticamente no retorno – a próxima chamada awaitbloqueará novamente até o próximoset().clear()– limpa explicitamente a flag sem aguardá-la. Útil antes de um primeirowaitpara descartar qualquerset()espúrio e prematuro que tenha disparado antes de a tarefa estar pronta.
8.9.4. Apenas uma tarefa em espera¶
Apenas uma tarefa pode estar em wait() por vez. O formato que uma aplicação deseja é uma única tarefa proprietária da flag – geralmente a mesma tarefa que instalou o handler de interrupção – com outras tarefas se comunicando com ela por meio de Event, Lock ou estruturas de dados compartilhadas protegidas por essas primitivas.
Quando um botão precisa acordar várias tarefas, a tarefa proprietária é o lugar para fazer a distribuição: o watcher acima poderia chamar some_event.set() após cada pressionamento, e todas as tarefas aguardando em some_event retomariam.