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).