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 จะทำงานต่อ