8.9. ThreadSafeFlag

asyncio.ThreadSafeFlag คือ primitive การส่งสัญญาณที่ asyncio มีไว้สำหรับกรณีที่ Event ไม่ รองรับ: interrupt handler ต้องการปลุกงาน asyncio ขึ้นมา interrupt ยิงนอก normal scheduling ของ event loop ดังนั้น bookkeeping ที่ asyncio.Event.set() ทำไม่สามารถทำได้อย่างปลอดภัยจากภายในมัน

8.9.1. ทำไมต้องมี primitive แยกต่างหาก

asyncio.Event.set() ดำเนิน bookkeeping ที่ปลุก coroutine ที่กำลังรอโดยถือว่า มันถูกเรียกจากภายในงานที่ loop กำลังรันอยู่ interrupt handler ไม่ตรงตามข้อสมมตินั้น -- มันอาจยิงระหว่างคำสั่งสองคำสั่งใดก็ได้และทำให้เสียหายสิ่งที่ loop กำลังทำกลางคัน

asyncio.ThreadSafeFlag.set() ถูกเขียนให้ปลอดภัยในบริบทเหล่านั้น การแลกเปลี่ยนสำหรับความปลอดภัยนั้นคือชุดคุณสมบัติที่เล็กกว่า Event: ThreadSafeFlag สามารถรอได้โดย งานเดียว ในแต่ละขณะ และ flag จะ auto-reset เมื่องานที่รออยู่ตื่นขึ้นมา

8.9.2. รูปแบบพื้นฐาน

การใช้งานทั่วไปคือ GPIO interrupt ส่งต่อ button-press event ไปยังงาน 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 ทำงานใน interrupt context เมื่อ GPIO ยิง; สิ่งที่มันทำคือเรียก flag.set() เท่านั้น งาน asyncio watcher await flag.wait() ในลูป; ทุกครั้งที่ flag ถูก set งานจะทำงานต่อ, รันงานที่การกดปุ่มกำหนด, แล้วลูปกลับไปรออีกครั้ง

8.9.3. สามเมธอด

  • set() -- ทำเครื่องหมาย flag ว่า set ถ้างานกำลังรอใน wait() อยู่, กำหนดให้มันทำงานต่อ ปลอดภัยที่จะเรียกจาก interrupt handler

  • wait() -- coroutine บล็อกจนกว่า flag จะถูก set แล้วคืนค่า flag จะ ถูก clear โดยอัตโนมัติ เมื่อคืนค่า -- การเรียก wait ครั้งต่อไปจะบล็อกอีกครั้งจนกว่าจะมี set() ครั้งต่อไป

  • clear() -- clear flag อย่างชัดเจนโดยไม่รอมัน มีประโยชน์ก่อน wait ครั้งแรกเพื่อทิ้ง set() ที่เกิดขึ้นก่อนกำหนดก่อนที่งานจะพร้อม

8.9.4. Waiter เดียวเท่านั้น

มีเพียงงานเดียวที่อาจอยู่ใน wait() ในแต่ละขณะ รูปแบบที่แอปพลิเคชันต้องการคืองาน owner เดียวสำหรับ flag -- มักเป็นงานเดียวกับที่ติดตั้ง interrupt handler -- โดยมีงานอื่นสื่อสารกับมันผ่าน Event, Lock, หรือโครงสร้างข้อมูลที่ใช้ร่วมกันที่ protected ด้วย primitive เหล่านั้น

เมื่อปุ่มต้องปลุกหลายงาน งาน owner คือที่ที่เหมาะสมสำหรับ fan out: watcher ข้างต้นสามารถเรียก some_event.set() หลังแต่ละการกด และทุกงานที่รอ some_event จะทำงานต่อ