8.7. אירועים

asyncio.Event הוא פרימיטיב האיתות הפשוט ביותר שהמודול מספק. קורוטינה אחת מגדירה (sets) אירוע כאשר משהו קרה; כל מספר של קורוטינות אחרות ממתינות שהאירוע יוגדר לפני שימשיכו. אין מטען נתונים – אירוע הוא בוליאני שמתהפך למצב פעיל ונשאר פעיל עד שמשהו מנקה אותו.

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

משימת הממתין רצה, מגיעה ל-await evt.wait(), ומושהית – האירוע מתחיל במצב מנוקה, ולכן קריאת ה-wait מוסרת שליטה בחזרה ללולאה. שנייה מאוחר יותר main קוראת ל-evt.set() והממתין מתוזמן להמשיך. ה-await asyncio.sleep(0) בסוף הוא רק מסירת שליטה כדי שהלולאה תקבל הזדמנות להריץ את הממתין לפני ש-main חוזרת.

8.7.2. ארבע המתודות

  • set() – מתהפך את האירוע למצב פעיל, ומתזמן כל קורוטינה החסומה כעת על wait() להמשיך. האירוע נשאר פעיל עד שמנוקה.

  • clear() – מתהפך את האירוע בחזרה למצב מנוקה. קורוטינות שמבצעות await wait() לאחר מכן ייחסמו שוב.

  • wait() – קורוטינה. אם האירוע כבר מוגדר, חוזרת מיד. אחרת חוסמת עד שמשהו מגדיר אותו.

  • is_set() – מחזירה את המצב הנוכחי ללא חסימה. שימושי כאשר קורוטינה רוצה לבדוק את האירוע מבלי להמתין לו.

8.7.3. ממתינים מרובים

מספר קורוטינות יכולות לבצע await על אותו אירוע. כאשר משהו קורא ל-set() כולן מתוזמנות להמשיך. האירוע הוא אחד-לרבים כברירת מחדל; אין צורך במספר אירועים כדי להתפצל למספר ממתינים.

8.7.4. שימוש חוזר באירוע

דפוס נפוץ הוא שימוש באירוע שוב ושוב – קורוטינה שרצה פעם אחת לכל איתות – על ידי ניקויו לאחר כל מעבר:

async def worker(go):
    while True:
        await go.wait()
        do_one_unit_of_work()
        go.clear()

היצרן מגדיר את האירוע בכל פעם שיש עבודה; העובד מנקה אותו לאחר שקלט את האיתות. אם היצרן עשוי להגדיר את האירוע שוב לפני שהעובד התעורר, ייתכן שהעובד ירצה לטפל במקרה זה (while go.is_set(): ...) לפני הניקוי.

8.7.5. הקשרים בטוחים

Event אינו בטוח לקריאת set() עליו מתוך מטפל פסיקה. המנגנון שבו הוא משתמש לתזמון ממתינים מניח שהוא רץ בתוך ההקשר של לולאת האירועים עצמה, מה שפסיקות אינן. כדי להעיר משימת asyncio ממטפל פסיקה, ThreadSafeFlag (מכוסה בקרוב) הוא הפרימיטיב הנכון.