نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/Python
Python

asyncio في Python: لماذا يخطئ المطورون في استخدامه حتى بعد قراءتهم للدوك?

asyncio ليس مجرد أداة لكتابة كود غير متزامن، بل هو نظام متكامل يتطلب فهمًا عميقًا للـ Event Loop والـ I/O Bound. اكتشف لماذا يخطئ حتى المطورون ذوو الخبرة في استخدامه، وكيف تتجنب الكوارث مثل الـ Blocking Calls والـ Memory Leaks في الإنتاج.

فريق نوفيل٩ يوليو ٢٠٢٦7 دقائق قراءة١٦ مشاهدة

في أحد المشاريع الكبيرة لشركة ناشئة في مجال الفنتك، كنا نستخدم asyncio لمعالجة آلاف الطلبات المتزامنة على سيرفر واحد. الكود كان يبدو نظيفًا ومُحسّنًا، لكن فجأة بدأ السيرفر يتجمد بشكل عشوائي كل بضع ساعات. بعد أيام من Debugging، اكتشفنا أن أحد المطورين استخدم دالة sync داخل كوروتين دون أن يدرك أنها ستوقف الـ Event Loop بالكامل. النتيجة؟ خسارة آلاف الدولارات بسبب توقف الخدمة، وكل ذلك لأن أحدهم ظن أن asyncio مجرد أداة لكتابة كود غير متزامن دون فهم عميق لكيفية عمله خلف الكواليس.

الحقيقة هي أن asyncio في Python ليس مجرد بديل لجمل الـ threading أو الـ multiprocessing، بل هو نظام متكامل يعتمد على مفهوم الـ Event Loop الذي يدير تنفيذ الكوروتينات بشكل غير متزامن. المشكلة الأكبر التي أراها يوميًا هي أن المطورين يقرؤون الدوك، يكتبون كودًا يبدو صحيحًا، ثم يفاجؤون بأن الأداء أسوأ مما كان عليه مع الكود المتزامن العادي. لماذا؟ لأن معظمهم لا يفهمون الفرق بين الـ I/O Bound والـ CPU Bound، أو كيف يتفاعل الـ Event Loop مع النظام الأساسي.

asyncio ليس سحرًا: كيف يعمل خلف الكواليس؟

عندما تكتب كودًا باستخدام asyncio، فأنت لا تكتب كودًا متوازيًا بالمعنى التقليدي، بل تكتب كودًا تعاونيًا غير متزامن. الـ Event Loop هو قلب النظام، وهو مسؤول عن إدارة تنفيذ الكوروتينات وتوزيع الوقت بينها بناءً على حالة الـ I/O. لكن هنا تكمن المشكلة: الـ Event Loop لا يستطيع التبديل بين الكوروتينات إلا عندما تصل إحدى الكوروتينات إلى نقطة انتظار (await). إذا استخدمت دالة sync داخل كوروتين، فإن الـ Event Loop سيتوقف تمامًا حتى تنتهي تلك الدالة، وهذا ما يُعرف بالـ Blocking Call.

لنأخذ مثالًا عمليًا: تخيل أنك تكتب سيرفر HTTP بسيط باستخدام aiohttp. إذا استخدمت دالة مثل time.sleep(5) داخل كوروتين، فإن السيرفر سيتوقف عن الاستجابة لأي طلبات جديدة لمدة 5 ثوانٍ كاملة، لأن time.sleep هو دالة sync تتحكم في الـ CPU ولا تسمح للـ Event Loop بالتبديل إلى كوروتينات أخرى. الحل؟ استخدام await asyncio.sleep(5) بدلاً من ذلك. لكن حتى هذا ليس كافيًا إذا لم تفهم كيف يتعامل الـ Event Loop مع النظام الأساسي.

python
# مثال خاطئ: استخدام دالة sync داخل كوروتين
import asyncio
import time

async def fetch_data():
 print("Fetching data...")
 time.sleep(2) # هذا سيوقف الـ Event Loop بالكامل!
 print("Data fetched")

async def main():
 await asyncio.gather(fetch_data(), fetch_data())

asyncio.run(main())
# النتيجة: الكوروتينات ستنفذ بالتتابع، وليس بالتوازي

# الحل الصحيح: استخدام await مع الدوال غير المتزامنة
async def fetch_data_correct():
 print("Fetching data...")
 await asyncio.sleep(2) # يسمح للـ Event Loop بالتبديل
 print("Data fetched")

asyncio.run(asyncio.gather(fetch_data_correct(), fetch_data_correct()))
# النتيجة: الكوروتينات ستنفذ بشكل متزامن

الـ Blocking Calls: القاتل الصامت لأداء asyncio

أكبر خطأ أراه في الكود الذي يستخدم asyncio هو استخدام دوال sync داخل كوروتينات دون وعي. المشكلة ليست فقط في دوال مثل time.sleep، بل في أي دالة تتحكم في الـ CPU أو الـ I/O بشكل متزامن. على سبيل المثال، استخدام مكتبات مثل requests أو psycopg2 داخل كوروتينات دون استخدام النسخ غير المتزامنة منها (مثل aiohttp أو asyncpg) سيؤدي إلى توقف الـ Event Loop بالكامل. هذا النوع من الأخطاء يصعب اكتشافه في بيئات التطوير، لكنه يظهر بوضوح في الإنتاج عندما يزداد الحمل على السيرفر.

في أحد المشاريع التي عملت عليها، استخدم فريق التطوير مكتبة requests داخل كوروتينات لجلب بيانات من واجهة برمجة تطبيقات خارجية. الكود كان يعمل بشكل جيد في بيئة التطوير مع عدد قليل من الطلبات، لكن عندما تم نشره في الإنتاج، بدأ السيرفر يتجمد بشكل عشوائي. بعد تحليل السجلات، اكتشفنا أن الـ Event Loop كان يتوقف تمامًا أثناء انتظار استجابة من الـ API الخارجي، مما أدى إلى تراكم الطلبات وانهيار السيرفر. الحل؟ استبدال requests بـ aiohttp، لكن حتى هذا لم يكن كافيًا دون فهم عميق لكيفية إدارة الـ Event Loop للطلبات.

python
# مثال خاطئ: استخدام requests داخل كوروتين
import asyncio
import requests

async def fetch_api_wrong(url):
 resp requests.get(url) # هذا سيوقف الـ Event Loop!
 return response.json()

# الحل الصحيح: استخدام aiohttp
import aiohttp

async def fetch_api_correct(url):
 async with aiohttp.ClientSession() as session:
 async with session.get(url) as response:
 return await response.json()

# مثال على كيفية استخدامه
async def main():
 urls = ["https://api.example.com/data1", "https://api.example.com/data2"]
 tasks = [fetch_api_correct(url) for url in urls]
 results = await asyncio.gather(*tasks)
 print(results)

asyncio.run(main())

كيف تتجنب الـ Blocking Calls؟

  • •استخدم دائمًا النسخ غير المتزامنة من المكتبات (مثل aiohttp بدلاً من requests، asyncpg بدلاً من psycopg2).
  • •إذا اضطررت لاستخدام مكتبة sync، قم بتشغيلها في thread منفصل باستخدام asyncio.to_thread أو loop.run_in_executor.
  • •تجنب استخدام دوال sync تتحكم في الـ CPU لفترات طويلة داخل كوروتينات.
  • •استخدم أدوات مثل aiomonitor لمراقبة الـ Event Loop وتحديد الـ Blocking Calls في بيئة التطوير.
  • •قم باختبار الكود تحت حمل حقيقي باستخدام أدوات مثل locust أو k6 قبل النشر في الإنتاج.

الـ Event Loop: القلب النابض لـ asyncio الذي لا يفهمه الكثيرون

الـ Event Loop في asyncio ليس مجرد حلقة تكرارية بسيطة، بل هو نظام معقد يدير تنفيذ الكوروتينات، الجدولة، وإدارة الموارد. المشكلة الأكبر هي أن معظم المطورين لا يفهمون كيف يتعامل الـ Event Loop مع النظام الأساسي، خاصة عندما يتعلق الأمر بالـ I/O. على سبيل المثال، عندما تستخدم await مع دالة غير متزامنة مثل aiohttp.get، فإن الـ Event Loop لا ينتظر بشكل سلبي، بل يقوم بجدولة الكوروتين الحالي جانبًا ويستمر في تنفيذ كوروتينات أخرى حتى يتم استلام البيانات من الشبكة.

لكن ماذا يحدث إذا كان هناك الآلاف من الكوروتينات التي تنتظر البيانات في نفس الوقت؟ هنا يأتي دور الـ Backpressure. إذا لم تدير الـ Event Loop بشكل صحيح، فقد ينتهي بك الأمر إلى استهلاك كل ذاكرة النظام بسبب تراكم الكوروتينات التي تنتظر البيانات. هذا ما حدث في أحد المشاريع التي عملت عليها، حيث استخدم الفريق asyncio لمعالجة آلاف الملفات الكبيرة في وقت واحد دون تحديد حد أقصى لعدد الكوروتينات المتزامنة. النتيجة؟ الـ Memory Leak الذي استهلك كل ذاكرة السيرفر وأدى إلى توقف الخدمة.

python
# مثال على إدارة الـ Backpressure باستخدام Semaphore
import asyncio

async def process_file(file_path, semaphore):
 async with semaphore:
 print(f"Processing {file_path}")
 await asyncio.sleep(1) # محاكاة معالجة الملف
 print(f"Finished {file_path}")

async def main():
 semaphore = asyncio.Semaphore(10) # حد أقصى 10 كوروتينات متزامنة
 files = [f"file_{i}.txt" for i in range(100)]
 tasks = [process_file(file, semaphore) for file in files]
 await asyncio.gather(*tasks)

asyncio.run(main())

كيف تدير الـ Event Loop بشكل صحيح؟

  • •حدد دائمًا عدد الكوروتينات المتزامنة باستخدام أدوات مثل Semaphore أو asyncio.gather مع chunking.
  • •تجنب إنشاء عدد كبير من الكوروتينات في وقت واحد، خاصة إذا كانت تنتظر موارد خارجية.
  • •استخدم أدوات مثل aioconsole لمراقبة حالة الـ Event Loop في الوقت الفعلي.
  • •افهم الفرق بين الـ Event Loop في بيئات التطوير والإنتاج، خاصة فيما يتعلق بإدارة الموارد.
  • •قم باختبار الكود تحت ظروف مشابهة للإنتاج باستخدام أدوات مثل pytest-asyncio.

الـ CPU Bound Tasks: متى تفشل asyncio؟

asyncio مصمم بشكل أساسي للتعامل مع المهام التي تعتمد على الـ I/O، مثل جلب البيانات من الشبكة أو قراءة الملفات. لكن عندما يتعلق الأمر بالمهام التي تعتمد على الـ CPU، مثل معالجة الصور أو الحسابات الرياضية المعقدة، فإن asyncio يصبح عديم الفائدة تقريبًا. السبب؟ لأن الـ Event Loop لا يستطيع التبديل بين الكوروتينات أثناء تنفيذ مهمة تعتمد على الـ CPU، مما يعني أن الكوروتين الذي يقوم بحساب معقد سيوقف الـ Event Loop بالكامل حتى ينتهي من المهمة.

في أحد المشاريع التي عملت عليها، استخدم الفريق asyncio لمعالجة آلاف الصور باستخدام مكتبة PIL. الكود كان يبدو صحيحًا، لكن الأداء كان أسوأ بكثير من الكود المتزامن العادي. بعد تحليل المشكلة، اكتشفنا أن معالجة الصور تعتمد بشكل كامل على الـ CPU، مما يعني أن الـ Event Loop كان يتوقف تمامًا أثناء معالجة كل صورة. الحل؟ استخدام multiprocessing بدلاً من asyncio لهذه المهام، أو تقسيم العمل بين عدة عمليات باستخدام ProcessPoolExecutor.

python
# مثال خاطئ: استخدام asyncio لمهمة تعتمد على الـ CPU
import asyncio
from PIL import Image, ImageFilter

async def process_image_wrong(image_path):
 img = Image.open(image_path)
 img = img.filter(ImageFilter.BLUR) # هذه المهمة تعتمد على الـ CPU!
 img.save(f"processed_{image_path}")

# الحل الصحيح: استخدام multiprocessing
import concurrent.futures

def process_image_sync(image_path):
 img = Image.open(image_path)
 img = img.filter(ImageFilter.BLUR)
 img.save(f"processed_{image_path}")

async def main():
 with concurrent.futures.ProcessPoolExecutor() as pool:
 loop = asyncio.get_running_loop()
 tasks = [loop.run_in_executor(pool, process_image_sync, f"image_{i}.jpg") for i in range(10)]
 await asyncio.gather(*tasks)

asyncio.run(main())

الـ Memory Leaks: كيف تدمر مشروعك دون أن تدري؟

أحد أكبر الأخطاء التي يقع فيها المطورون عند استخدام asyncio هو تجاهل إدارة الذاكرة. لأن الكوروتينات خفيفة الوزن، فمن السهل جدًا إنشاء الآلاف منها دون التفكير في استهلاك الذاكرة. لكن المشكلة الأكبر هي أن الكوروتينات التي لا تنتهي بشكل صحيح يمكن أن تؤدي إلى تسرب الذاكرة. على سبيل المثال، إذا أنشأت كوروتينًا ينتظر حدثًا لا يحدث أبدًا، فإن هذا الكوروتين سيبقى في الذاكرة إلى الأبد، مما يؤدي إلى زيادة استهلاك الذاكرة تدريجيًا حتى ينهار السيرفر.

في أحد المشاريع التي عملت عليها، استخدم الفريق asyncio لإنشاء نظام مراقبة في الوقت الفعلي. الكود كان يبدو صحيحًا، لكن بعد بضعة أيام من التشغيل المستمر، بدأ السيرفر يعاني من ارتفاع استهلاك الذاكرة. بعد تحليل السجلات باستخدام أدوات مثل tracemalloc، اكتشفنا أن هناك الآلاف من الكوروتينات التي لم تنتهي بشكل صحيح بسبب خطأ في منطق البرنامج. الحل؟ استخدام timeouts مع جميع الكوروتينات التي تنتظر أحداثًا خارجية، والتأكد من إغلاق جميع الموارد بشكل صحيح.

python
# مثال على استخدام Timeout لتجنب الـ Memory Leaks
import asyncio

async def fetch_data_with_timeout(url, timeout=5):
 try:
 async with asyncio.timeout(timeout):
 # محاكاة جلب البيانات
 await asyncio.sleep(10) # هذه المهمة ستفشل بسبب الـ Timeout
 return "Data"
 except asyncio.TimeoutError:
 print(f"Timeout while fetching {url}")
 return None

async def main():
 tasks = [fetch_data_with_timeout(f"https://api.example.com/data{i}") for i in range(10)]
 results = await asyncio.gather(*tasks)
 print(results)

asyncio.run(main())

كيف تتجنب الـ Memory Leaks في asyncio؟

  • •استخدم دائمًا timeouts مع الكوروتينات التي تنتظر أحداثًا خارجية.
  • •تأكد من إغلاق جميع الموارد (مثل اتصالات الشبكة أو الملفات) بشكل صحيح باستخدام context managers.
  • •استخدم أدوات مثل tracemalloc لمراقبة استهلاك الذاكرة في بيئة التطوير.
  • •قم باختبار الكود تحت ظروف مشابهة للإنتاج لفترات طويلة لتحديد أي تسرب للذاكرة.
  • •تجنب إنشاء عدد كبير من الكوروتينات في وقت واحد دون تحديد حد أقصى.

خلاصة المهندس: نصائح لا غنى عنها لاستخدام asyncio بشكل صحيح

asyncio أداة قوية، لكنها ليست حلاً سحريًا لجميع المشاكل. إذا كنت تريد استخدامها بشكل صحيح، فعليك فهم كيف يعمل الـ Event Loop خلف الكواليس، وتجنب الـ Blocking Calls بأي ثمن، وإدارة الموارد بحذر. لا تستخدم asyncio لمهام تعتمد على الـ CPU، واستخدم multiprocessing بدلاً من ذلك. راقب دائمًا استهلاك الذاكرة، واستخدم timeouts مع جميع الكوروتينات التي تنتظر أحداثًا خارجية. وأخيرًا، اختبر الكود تحت ظروف مشابهة للإنتاج قبل النشر، لأن الأخطاء التي لا تظهر في التطوير قد تدمر مشروعك في الإنتاج.

في النهاية، asyncio ليس مجرد أداة لكتابة كود غير متزامن، بل هو نظام متكامل يتطلب فهمًا عميقًا لكيفية عمله. إذا استخدمت بشكل صحيح، يمكنه تحسين أداء تطبيقاتك بشكل كبير. لكن إذا استخدمت بشكل خاطئ، فقد يؤدي إلى كوارث في الإنتاج. لذا، خذ وقتك لفهم المفاهيم الأساسية، واختبر كودك بعناية، ولا تفترض أبدًا أن الكود الذي يعمل في التطوير سيعمل في الإنتاج.

asyncio Python برمجة غير متزامنة Event Loop I/O Bound Memory Leak

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر