تكرار السؤال: …
الإجابة المختصرة
الإلغاء تعاوني: task.cancel() ترمي CancelledError داخل المهمة عند نقطة await التالية، ويجب تنظيف الموارد في finally ثم ترك CancelledError ينتشر وعدم ابتلاعه.
الشرح
منذ Python 3.8 ترث CancelledError من BaseException، فلا تلتقطها except Exception، وهذا مقصود. ابتلاع الإلغاء يجعل الإغلاق (shutdown) والمهلات الزمنية تتعطل لأن الـ caller ينتظر مهمة لن تنتهي. استخدم asyncio.shield فقط لحماية جزء صغير لا يجوز قطعه (مثل commit)، مع العلم أن الـ caller نفسه سيُلغى. وتحتاج الحلقات الحسابية الطويلة إلى await asyncio.sleep(0) دوريًا لتكون قابلة للإلغاء.
مثال بالكود
import asyncio
async def worker(queue: asyncio.Queue):
conn = await open_connection()
try:
while True:
job = await queue.get()
await conn.send(job)
finally:
await conn.close() # يُنفذ عند الإلغاء أيضًا
task = asyncio.create_task(worker(q))
task.cancel()أخطاء شائعة
- كتابة except BaseException: pass أو except asyncio.CancelledError: pass فيتعطل الإيقاف.
- حلقة حسابية بلا أي await فلا يمكن إلغاؤها.