8.7. Events¶
asyncio.Event คือ primitive การส่งสัญญาณที่ง่ายที่สุดที่โมดูลมีให้ coroutine หนึ่ง set event เมื่อบางอย่างเกิดขึ้น; coroutine อื่นๆ จำนวนเท่าใดก็ได้ รอ ให้ event ถูก set ก่อนดำเนินต่อ ไม่มี payload -- event คือ boolean ที่พลิกเป็น on และอยู่ on จนกว่าบางอย่างจะ clear มัน
8.7.1. รูปแบบพื้นฐาน¶
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())
งาน waiter ทำงาน, ถึง await evt.wait(), และถูกระงับ -- event เริ่มต้นในสถานะ cleared ดังนั้นการเรียก wait จะ yield กลับไปที่ loop วินาทีต่อมา main เรียก evt.set() และ waiter ถูกกำหนดให้ทำงานต่อ await asyncio.sleep(0) ท้ายนั้นเป็นแค่ yield เพื่อให้ loop มีโอกาสรัน waiter ก่อนที่ main จะคืนค่า
8.7.2. สี่เมธอด¶
set()-- พลิก event ให้ set และกำหนดให้ทุก coroutine ที่บล็อกอยู่ที่wait()ทำงานต่อ event ยังคง set จนกว่าจะถูก clearedclear()-- พลิก event กลับไปที่ cleared coroutine ที่await wait()หลังจากนี้จะบล็อกอีกครั้งwait()-- coroutine ถ้า event ถูก set แล้ว จะคืนค่าทันที มิฉะนั้นจะบล็อกจนกว่าบางอย่างจะ set มันis_set()-- คืนค่าสถานะปัจจุบันโดยไม่บล็อก มีประโยชน์เมื่อ coroutine ต้องการ ตรวจสอบ event โดยไม่รอมัน
8.7.3. หลาย waiter¶
coroutine หลายตัวสามารถ await event เดียวกันได้ เมื่อบางอย่างเรียก set() พวกมัน ทั้งหมด ถูกกำหนดให้ทำงานต่อ event เป็น one-to-many โดยค่าเริ่มต้น ไม่จำเป็นต้องมีหลาย event เพื่อส่งสัญญาณออกไปยังหลาย waiter
8.7.4. การใช้ event ซ้ำ¶
รูปแบบทั่วไปคือการใช้ event ซ้ำ -- coroutine ที่ทำงานครั้งละหนึ่งสัญญาณ -- โดยการ clear มันหลังแต่ละรอบ:
async def worker(go):
while True:
await go.wait()
do_one_unit_of_work()
go.clear()
producer จะ set event เมื่อมีงาน; worker จะ clear มันเมื่อรับสัญญาณแล้ว หาก producer อาจ set event อีกครั้งก่อนที่ worker จะตื่นขึ้น worker อาจต้องจัดการกรณีนั้น (while go.is_set(): ...) ก่อน clear
8.7.5. บริบทที่ปลอดภัย¶
Event ไม่ ปลอดภัยที่จะเรียก set() จากภายใน interrupt handler กลไกที่มันใช้กำหนดให้ waiter ทำงานต่อนั้นถือว่ามันกำลังทำงานภายใน context ของ event loop เอง ซึ่ง interrupt ไม่ได้อยู่ใน context นั้น สำหรับการปลุกงาน asyncio จาก interrupt handler ThreadSafeFlag (ที่จะกล่าวถึงในไม่ช้า) คือ primitive ที่ถูกต้อง