8.9. ThreadSafeFlag¶
asyncio.ThreadSafeFlag to prymityw sygnalizacyjny, który asyncio udostępnia dla przypadku, którego Event nie obsługuje: procedura obsługi przerwania musi obudzić zadanie asyncio. Przerwanie wyzwala się poza normalnym harmonogramowaniem pętli zdarzeń, więc ewidencja prowadzona przez asyncio.Event.set() nie może być bezpiecznie wykonana z jego wnętrza.
8.9.1. Dlaczego oddzielny prymityw¶
asyncio.Event.set() prowadzi ewidencję budzącą oczekujące korutyny przy założeniu, że jest wywoływana z wnętrza zadania aktualnie uruchamianego przez pętlę. Procedura obsługi przerwania nie spełnia tego założenia – może wyzwolić się pomiędzy dowolnymi dwiema instrukcjami i uszkodzić to, co pętla właśnie miała w połowie wykonane.
asyncio.ThreadSafeFlag.set() jest napisana tak, by była bezpieczna w takich kontekstach. Ceną za to bezpieczeństwo jest mniejszy zestaw funkcji niż w Event: na ThreadSafeFlag może oczekiwać tylko jedno zadanie naraz, a flaga jest automatycznie resetowana, gdy oczekujące zadanie się budzi.
8.9.2. Podstawowy kształt¶
Typowym zastosowaniem jest przekazanie przez przerwanie GPIO zdarzenia naciśnięcia przycisku do zadania 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 działa w kontekście przerwania za każdym razem, gdy GPIO się wyzwala; wszystko, co robi, to wywołanie flag.set(). Zadanie asyncio watcher wykonuje await na flag.wait() w pętli; za każdym razem, gdy flaga zostaje ustawiona, zadanie wznawia działanie, wykonuje pracę wymaganą przez naciśnięcie przycisku, a następnie wraca do oczekiwania.
8.9.3. Trzy metody¶
set()– oznacza flagę jako ustawioną. Jeśli jakieś zadanie aktualnie oczekuje wwait(), planuje jego wznowienie. Bezpieczna do wywołania z procedury obsługi przerwania.wait()– korutyna. Blokuje, dopóki flaga nie zostanie ustawiona, a następnie zwraca sterowanie. Flaga jest automatycznie czyszczona przy powrocie – kolejne wywołaniewaitponownie zablokuje aż do następnegoset().clear()– jawnie czyści flagę bez oczekiwania na nią. Przydatne przed pierwszymwait, aby odrzucić wszelkie błędne, przedwczesneset(), które wyzwoliły się, zanim zadanie było gotowe.
8.9.4. Tylko jeden oczekujący¶
W wait() może znajdować się tylko jedno zadanie naraz. Kształt, jakiego oczekuje aplikacja, to pojedyncze zadanie właściciel flagi – zwykle to samo zadanie, które zainstalowało procedurę obsługi przerwania – z innymi zadaniami komunikującymi się z nim za pośrednictwem Event, Lock lub współdzielonych struktur danych chronionych przez te prymitywy.
Gdy przycisk musi obudzić kilka zadań, miejscem na rozdzielenie jest zadanie właściciela: powyższy watcher mógłby po każdym naciśnięciu wywołać some_event.set(), a wszystkie zadania oczekujące na some_event wznowiłyby działanie.