8.9. ThreadSafeFlag¶
asyncio.ThreadSafeFlag هو بدائية الإشارة التي يوفرها asyncio للحالة التي لا يدعمها Event: حاجة معالج المقاطعة إلى إيقاظ مهمة asyncio. تُطلق المقاطعة خارج الجدولة الطبيعية لحلقة الأحداث، لذا لا يمكن تنفيذ أعمال التدبير التي يقوم بها asyncio.Event.set() بأمان من داخلها.
8.9.1. لماذا بدائية منفصلة¶
ينفّذ asyncio.Event.set() أعمال التدبير التي توقظ الإجراءات المتزامنة المنتظِرة على افتراض أنه يُستدعى من داخل مهمة تشغّلها الحلقة حاليًا. ومعالج المقاطعة لا يحقق هذا الافتراض -- إذ يمكن أن يُطلق بين أي تعليمتين ويفسد أي شيء كانت الحلقة في منتصف تنفيذه.
كُتب asyncio.ThreadSafeFlag.set() ليكون آمنًا في تلك السياقات. والمقايضة مقابل ذلك الأمان هي مجموعة ميزات أصغر من Event: إذ لا يمكن لـ ThreadSafeFlag أن تنتظر عليه إلا مهمة واحدة في كل مرة، وتُعاد العلامة إلى وضعها الأصلي تلقائيًا عند إيقاظ المهمة المنتظِرة.
8.9.2. الشكل الأساسي¶
من الاستخدامات النموذجية تسليم مقاطعة GPIO حدثَ ضغطة زر إلى مهمة 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 في سياق المقاطعة كلما أُطلقت GPIO؛ وكل ما تفعله هو استدعاء flag.set(). وتنفّذ مهمة asyncio المسماة watcher العبارة await على flag.wait() ضمن حلقة؛ وفي كل مرة تُضبط فيها العلامة، تستأنف المهمة عملها، وتنفّذ ما تتطلبه ضغطة الزر، ثم تعود إلى الانتظار من جديد.
8.9.3. الطرق الثلاث¶
set()-- تضبط العلامة على أنها مضبوطة. وإذا كانت هناك مهمة تنتظر حاليًا فيwait()، فإنها تُجدوَل للاستئناف. آمنة للاستدعاء من معالج مقاطعة.wait()-- إجراء متزامن. يحجب حتى تُضبط العلامة، ثم يعود. وتُمسح العلامة تلقائيًا عند العودة -- إذ سيحجب الاستدعاء التالي لـwaitمن جديد حتى الاستدعاء التالي لـset().clear()-- تمسح العلامة صراحةً دون الانتظار عليها. مفيدة قبل أولwaitللتخلص من أي استدعاء زائف مبكر لـset()أُطلق قبل أن تكون المهمة جاهزة.
8.9.4. منتظِر واحد فقط¶
لا يمكن أن تكون أكثر من مهمة واحدة في wait() في الوقت نفسه. والشكل الذي يريده التطبيق هو مهمة مالكة واحدة للعلامة -- عادةً المهمة نفسها التي ثبّتت معالج المقاطعة -- مع تواصل المهام الأخرى معها عبر Event أو Lock أو هياكل بيانات مشتركة محمية بتلك البدائيات.
عندما يتعين على زر أن يوقظ عدة مهام، فإن المهمة المالكة هي المكان المناسب للتوزيع: إذ يمكن للمراقب أعلاه استدعاء some_event.set() بعد كل ضغطة، فتستأنف جميع المهام المنتظِرة على some_event عملها.