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 em wait(), 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 a wait bloqueará novamente até o próximo set().

  • clear() – limpa explicitamente a flag sem aguardá-la. Útil antes de um primeiro wait para descartar qualquer set() 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.