تكرار السؤال: …
الإجابة المختصرة
مررها إلى asyncio.to_thread (أو loop.run_in_executor) لتعمل في thread منفصل دون حجب الـ event loop، واستخدم ProcessPoolExecutor مع run_in_executor للأعمال الحسابية.
الشرح
to_thread مناسب لمكتبات I/O غير المتزامنة مثل SDK قديم أو قراءة ملفات كبيرة. يحسّن ذلك الاستجابة، لكنه لا يجعل الحسابات بلغة Python متوازية في CPython التقليدي بسبب الـ GIL. يستخدم to_thread الـ executor الافتراضي ذي العدد المحدود من الخيوط، لذلك إرسال آلاف الاستدعاءات الحاجبة سيصطف خلفه؛ حدد حجم pool مناسبًا أو ابحث عن بديل غير متزامن.
مثال بالكود
import asyncio
from concurrent.futures import ProcessPoolExecutor
async def handler(path: str):
data = await asyncio.to_thread(read_large_file, path) # I/O حاجب
loop = asyncio.get_running_loop()
with ProcessPoolExecutor() as pool:
digest = await loop.run_in_executor(pool, heavy_hash, data)
return digestأخطاء شائعة
- استدعاء دالة حاجبة مباشرة داخل coroutine فتتأخر كل الطلبات الأخرى.
- استخدام to_thread لعمل حسابي ثقيل وتوقع توازٍ حقيقي.
أسئلة متابعة
- ما هو event loop وما دور الـ coroutine في asyncio؟
- ما تأثير الـ GIL في CPython، وما الذي تغير مع النسخة free-threaded؟