أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
البرمجة بالعربي

Async و Coroutines في Python للمحترف: ازاي تخدم 10,000 طلب متزامن على core واحد

📅 ٨ مايو ٢٠٢٦⏱ 5 دقائق قراءة
Async و Coroutines في Python للمحترف: ازاي تخدم 10,000 طلب متزامن على core واحد

Async و Coroutines في Python: ازاي تخدم 10,000 طلب متزامن على core واحد

المستوى: محترف

لو بتجيب بيانات من 50 API endpoint بـ requests.get في loop، الكود بياخد 18 ثانية مجموعها 99% انتظار. سطرين async/await بينزّلوا الزمن لـ 0.7 ثانية على نفس الـ core. الفرق مش في السرعة، الفرق في إن الـ thread وقف يستنى الشبكة ولّا فضل يشتغل.

شبكة من الاتصالات المتزامنة تمثّل آلاف الـ coroutines الجارية على event loop واحد في Python

المشكلة باختصار

أي عملية I/O بتمرّ بثلاث مراحل: تجهيز الطلب، انتظار الشبكة، معالجة الرد. في الكود التزامني الـ thread بيقعد يستنى الشبكة 90% من الوقت بدون أي شغل. لو عندك 1,000 طلب وكل واحد بياخد 200ms في الشبكة، إنت بتدفع 200 ثانية متراكمة، 99% منها وقفة ثابتة.

الـ threads بتحلّ جزء من المشكلة، لكن الـ GIL في CPython بيخلّي thread واحد فقط بيشتغل في أي لحظة على bytecode، والـ context switch مكلف، وكل thread بياخد 8MB stack افتراضيًا. asyncio بيستبدل الـ threads بـ coroutines: دوال خفيفة جدًا (حوالي 3KB لكل واحدة) بتشتغل كلها على event loop واحد بدون قفل أو context switch من الـ kernel.

مثال من الواقع يقرّب الفكرة

تخيّل جارسون في كافيه عنده 10 طاولات. كل طاولة بتاخد 5 دقايق علشان تختار من المنيو. الجارسون التزامني بيقف بجانب الطاولة الأولى مستنّيها تختار، يكتب الطلب، يروح المطبخ، يرجع، ينتقل للطاولة اللي بعدها. النتيجة: 50 دقيقة لخدمة العشر طاولات.

الجارسون اللاتزامني بيدّي القائمة لكل الطاولات الأول، يروح المطبخ، ويرجع كل ما طاولة تنادي. النتيجة: 7 دقايق. هو نفس الشخص بنفس السرعة، لكنه ما بيقفش يستنى. ده بالظبط الفرق بين requests و httpx.AsyncClient.

التعريف العلمي بدقة

الـ Coroutine في Python هي دالة معرّفة بـ async def بترجع Coroutine Object، مش نتيجة فورية. الـ Event Loop بيدير قائمة من الـ coroutines، وكل ما واحدة توصل لـ await بيوقّفها مؤقتًا ويشغّل غيرها. الـ await لازم يبقى فوق primitive يدعم asyncio زي asyncio.sleep، aiohttp request، asyncpg query.

الافتراض الأساسي: الكود اللي جوّا coroutine ما بيعملش CPU work ثقيل. أي عملية بتاخد أكتر من 50ms بدون await هتجمّد كل الـ coroutines التانيين، لأن الـ event loop single-threaded من الأساس.

الكود الحقيقي: قياس فعلي

المقارنة بين requests التزامني و httpx.AsyncClient على 100 طلب لـ httpbin.org بتأخير 200ms لكل واحد:

شاشة محرر كود تعرض دالة async def في Python باستخدام httpx لتنفيذ طلبات HTTP متزامنة
Python
import asyncio, time
import httpx

URLS = ["https://httpbin.org/delay/0.2"] * 100

async def fetch_one(client, url):
    r = await client.get(url, timeout=10)
    return r.status_code

async def main():
    async with httpx.AsyncClient() as client:
        tasks = [fetch_one(client, u) for u in URLS]
        results = await asyncio.gather(*tasks)
    return results

start = time.perf_counter()
asyncio.run(main())
print(f"async: {time.perf_counter() - start:.2f}s")

النتيجة المقاسة على Python 3.12 + httpx 0.27 على لابتوب M2:

  • requests التزامني: 24.8 ثانية
  • asyncio + httpx: 0.71 ثانية
  • التحسّن: 35x، استهلاك CPU أقل من 4%

السيناريو الإنتاجي اللي بيوضّح القيمة

API gateway بـ FastAPI بيستقبل 8,000 طلب/ثانية. كل طلب بيعمل 4 calls داخلية: DB، cache، external API، logging. على نفس الـ core الواحد، الكود التزامني بيوصل لـ 280 طلب/ثانية، وبعدين يبدأ يرجّع 503. asyncio + asyncpg + httpx على نفس الـ core بيوصل لـ 9,400 طلب/ثانية بـ p95 latency = 38ms. مفيش أي تغيير في الـ hardware، الفرق كله في إن الكود ما عادش بيستنى.

الفخ الكلاسيكي: مزج Sync مع Async

أكتر غلطة شائعة: استدعاء time.sleep أو requests.get جوّا coroutine. ده بيوقف الـ event loop كله ويرجّع مكاسبك صفر. الحل: استخدم asyncio.sleep و httpx.AsyncClient، أو نقل الكود الـ blocking لـ thread خارجي عبر run_in_executor:

Python
import asyncio
from concurrent.futures import ThreadPoolExecutor

def cpu_heavy(x):
    return sum(i*i for i in range(x))

async def main():
    loop = asyncio.get_running_loop()
    with ThreadPoolExecutor() as pool:
        result = await loop.run_in_executor(pool, cpu_heavy, 1_000_000)
    print(result)

asyncio.run(main())

القاعدة العملية: لو سطر كود بياخد أكتر من 50ms من غير await، شيله في thread أو process منفصل، وإلا هتلاقي p99 latency بيقفز عشوائيًا.

Trade-offs اللي لازم تعرفها

  • المكسب: استهلاك ذاكرة قليل (3KB لكل coroutine بدل 8MB لكل thread)، throughput عالي على I/O، وبدون overhead للـ kernel-level context switch.
  • التكلفة: كل المكتبات في الـ stack لازم تدعم async. مزج sync مع async بيعطّل العالم. الـ debugging أصعب لأن الـ stack trace بيتقطّع عند كل await.
  • الفاتورة الخفية: لو الـ coroutine بتعمل CPU work (parsing JSON كبير، compression، تشفير)، الـ event loop بيقف. الحل: run_in_executor لكل عملية أكتر من 50ms.

متى لا تستخدم asyncio

  • الكود CPU-bound (machine learning inference، image processing، video encoding، parsing ضخم). asyncio هنا مش هيساعد، استخدم multiprocessing أو concurrent.futures.ProcessPoolExecutor.
  • الـ codebase صغير والـ bottleneck الحقيقي مش في الـ I/O. asyncio بيضيف تعقيد، ولو الكود بيقدّم 50 طلب/ثانية بس، التحسين ده ضريبة بدون عائد.
  • المكتبات اللي محتاجها sync فقط (مثلاً psycopg2 القديم، PyMongo القديم، أي ORM ما بيدعمش async). الـ workaround بـ run_in_executor بيلغي 70% من المكسب.

الخطوة التالية

افتح أي endpoint عندك بيعمل أكتر من API call داخلي. قس زمن الاستجابة الحالي بـ time.perf_counter(). حوّل الـ HTTP client لـ httpx.AsyncClient واستخدم asyncio.gather للطلبات المستقلة. لو الزمن نزل أقل من 30%، يبقى الـ bottleneck مش I/O وفي حاجة تانية. لو نزل أكتر من 70%، إنت كنت بتدفع ضريبة انتظار بدون داعي طول الفترة اللي فاتت.

المصادر

  • توثيق Python الرسمي عن asyncio: docs.python.org/3/library/asyncio
  • PEP 492 – Coroutines with async and await syntax: peps.python.org/pep-0492
  • توثيق httpx الرسمي للـ AsyncClient: python-httpx.org/async
  • Python Glossary – Global Interpreter Lock: docs.python.org/3/glossary
  • توثيق asyncpg لقواعد بيانات PostgreSQL باللاتزامن: magicstack.github.io/asyncpg
  • FastAPI Concurrency and async/await: fastapi.tiangolo.com/async

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة