معماری ناهمگام و مفهوم Event Loop
گذر از بنبستهای اجرایی (Blocking) و شناخت عمیق کوروتینها
در برنامهنویسی سنتی یا «همگام» (Synchronous)، وقتی کدی به عملیاتی زمانبر (مثلاً درخواست از شبکه، دانلود فایل، یا خواندن دیتابیس) میرسد، کل برنامهی شما و پردازنده متوقف میشود تا پاسخ دریافت گردد. این یعنی خطوط بعدی کد باید به اجبار منتظر بمانند که رم و پردازنده به شدت هدر میرود. اما با رویکرد «ناهمگام» (Asynchronous) توسط کتابخانه داخلی asyncio، در زمان انتظار یک عملیات، برنامهی شما میتواند کنترل را رها کرده و به اجرای بخشها و وظایف دیگر بپردازد!
حلقه رویداد (Event Loop) چیست؟
هسته مرکزی معماری Async در پایتون، **Event Loop** است. حلقه رویداد را میتوان به یک سرآشپزِ بینهایت فرز تشبیه کرد که روی دهها قابلمهی مختلف در حال پختوپز است. او نیازی ندارد زل بزند تا آب یک قابلمه جوش بیاید (Block شدن)؛ بلکه زیر آن را روشن میکند (await) و فوراً سراغ خرد کردن پیازِ غذایی دیگر میرود! هر زمان که آب جوش آمد، زنگی به صدا در میآید و حلقه رویداد متوجه میشود که باید ادامهی کار آن قابلمه را پی بگیرد.
معماری ناهمگام (asyncio) ذاتاً برای حل مشکلات گلوگاههای ورودی/خروجی (I/O-Bound) مثل درخواستهای هزاران کلاینت همزمان در یک سرور شبکه طراحی شده است. اگر برنامه شما درگیر پردازشهای فوقسنگینِ محاسباتی و جبری (CPU-Bound) است، معماریِ ناهمگام کمکی نمیکند، زیرا پردازنده واقعاً درگیر است و راهکارهای Multiprocessing نیاز خواهد بود.
async def | کلمه کلیدی برای تبدیل یک تابع معمولی به یک "کوروتین" (Coroutine). فراخوانی این توابع به تنهایی هیچ چیزی را اجرا نمیکند، بلکه تنها یک شیء زمانبندیشده خلق میکند. |
await | دستوری الزامی پیش از اجرای کوروتینها؛ اجرای کد در همان خط متوقف میشود تا نتیجه حاضر شود، اما همزمان نبضِ کنترل به Event Loop برمیگردد تا باقیِ سیستم آزاد باشد. |
asyncio.run() | موتور استارتِ پایتون! این تابع وظیفهی راهاندازی، نظارت و بستنِ نهایی حلقه رویداد (Event Loop) را در ریشه برنامه بر عهده دارد. |
asyncio.sleep() | نمادِ غیربرخوردکنندهی (Non-blocking) برای توقف زمان. کاملاً جایگزینِ time.sleep است که باعث میشود کل فرآیندهای برنامه برای لحظاتی به استراحت نروند. |
import asyncio
async def say_hello():
print("سلام...")
# برنامههای دیگر در این یک ثانیه متوقف نخواهند شد
await asyncio.sleep(1)
print("...پایتون!")
# نقطه شروع و ورود به قلمرو Async
asyncio.run(say_hello())دستور await در پایتون، تنها و تنها درون توابعی که با async def نشانه گذاری شدهاند قابل استفاده است. همچنین هرگز نباید در توابع Async از دستوراتِ سنتیِ مسدودکننده مانند time.sleep() یا requests.get() استفاده کنید؛ زیرا پردازشِ سرور قفل شده و معماریِ ناهمگام را تبدیل به فاجعه خواهد کرد.