8.7. Zdarzenia#
asyncio.Event to najprostszy prymityw sygnalizacyjny udostępniany przez moduł. Jedna korutyna ustawia zdarzenie, gdy coś się wydarzyło; dowolna liczba innych korutyn czeka na ustawienie zdarzenia przed kontynuowaniem. Nie ma ładunku – zdarzenie jest wartością logiczną, która przełącza się na włączoną i pozostaje włączona, dopóki coś jej nie wyczyści.
8.7.1. Podstawowy schemat#
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())
Zadanie oczekujące działa, trafia na await evt.wait() i zostaje wstrzymane – zdarzenie zaczyna w stanie wyczyszczonym, więc wywołanie wait oddaje sterowanie z powrotem do pętli. Sekundę później main wywołuje evt.set() i oczekujące zostaje zaplanowane do wznowienia. Końcowe await asyncio.sleep(0) to po prostu oddanie sterowania, aby pętla dostała szansę uruchomić oczekującego, zanim main powróci.
8.7.2. Cztery metody#
set()– przełącza zdarzenie na ustawione i planuje wznowienie każdej korutyny aktualnie zablokowanej nawait(). Zdarzenie pozostaje ustawione aż do wyczyszczenia.clear()– przełącza zdarzenie z powrotem na wyczyszczone. Korutyny, które po tym wykonająawait wait(), ponownie się zablokują.wait()– korutyna. Jeśli zdarzenie jest już ustawione, zwraca sterowanie natychmiast. W przeciwnym razie blokuje, dopóki coś go nie ustawi.is_set()– zwraca bieżący stan bez blokowania. Przydatne, gdy korutyna chce sprawdzić zdarzenie bez oczekiwania na nie.
8.7.3. Wielu oczekujących#
Kilka korutyn może wykonać await na tym samym zdarzeniu. Gdy coś wywoła set(), wszystkie zostają zaplanowane do wznowienia. Zdarzenie jest domyślnie jeden-do-wielu; nie ma potrzeby tworzenia kilku zdarzeń, by rozprowadzić je do kilku oczekujących.
8.7.4. Ponowne użycie zdarzenia#
Powszechnym wzorcem jest wielokrotne użycie zdarzenia – korutyna działająca raz na sygnał – poprzez czyszczenie go po każdym przebiegu:
async def worker(go):
while True:
await go.wait()
do_one_unit_of_work()
go.clear()
Producent ustawia zdarzenie, gdy tylko jest praca; worker czyści je, gdy odbierze sygnał. Jeśli producent może ponownie ustawić zdarzenie, zanim worker się obudzi, worker może chcieć obsłużyć ten przypadek (while go.is_set(): ...) przed czyszczeniem.
8.7.5. Bezpieczne konteksty#
Na Event nie można bezpiecznie wywołać set() z wnętrza funkcji obsługi przerwania. Mechanizm, którego używa do planowania oczekujących, zakłada, że działa wewnątrz własnego kontekstu pętli zdarzeń, czym przerwania nie są. Do wybudzania zadania asyncio z funkcji obsługi przerwania właściwym prymitywem jest ThreadSafeFlag (omówiony wkrótce).