تشریح مفاهیم Synchronous و Asynchronous به زبان ساده‌ی فارسی

فهرست مطالب

مقدمه

وقتی واژه‌ی Synchronous را در دیکشنری جستجو می‌کنید، چیزی که دستگیرتان می‌شود احتمالا این معنی است: هم‌زمان.

و به تناسب، برای واژه‌ی Asynchronous هم با معنی «غیر هم‌زمان» مواجه خواهید شد.

اگر در حال خواندن این نوشته هستید، احتمالا شما هم مثل من به این نتیجه رسیده‌اید که این معانی نمی توانند مفهوم این دو واژه را بدرستی منتقل نمایند. وقتی می خواهیم درباره‌ی این دو واژه در برنامه‌نویسی صحبت کنیم، متوجه خواهیم شد که «زمان» فقط یکی از فاکتورهایی است که در این موضوع دخیل است؛ حتی در خیلی از اوقات، قضیه «زمان» واقعا در اولویت نیست!

بیشتر نوشته های موجود در اینترنت نیز هنگام توضیح این دو مفهوم، به سرعت با عوامل جانبی مانند چگونگی پیاده‌سازی این‌ها در فلان زبان یا فلان فریم ورک خاص مخلوط می‌شوند و هیچوقت به درستی درکی از خود این مفاهیم صورت نمی گیرد. نتیجه این می‌شود که برنامه‌نویس‌ها از چنین قابلیت‌هایی در کد‌هایشان استفاده می کنند، بدون اینکه واقعا اصل داستان را بدانند.

من در این نوشته میخواهم به شیوه‌ی خودم این دو مفهوم پر استفاده را توضیح دهم؛ شاید این نوشته بتواند سودمند واقع شود.

منظور از کدهای Synchronous چیست

برای لحظه‌ای، موضوع «زمان» را به طور کامل از فکر خود بیرون کنید! بیاید از زاویه دیگری به مفهوم Synchronous نگاه کنیم؛

وقتی می‌گوییم کدهای ما Synchronous است، در حقیقت داریم درباره «ترتیب اجرای» کدها حرف می‌زنیم! به شکلی که این «ترتیب اجرا»، برابر با همان «ترتیب نگارش» کدهایمان باشد.

بنابراین «زمان» تنها فاکتور دخیل در Synchronous بودن نیست. در این مفهوم، «مکان» کدها و اینکه آن‌ها را به چه شیوه‌ای نوشته باشیم نیز از اهمیت برخوردار است.

وقتی می‌گوییم کدهای ما حالت Synchronous دارند، به این معنی است که ترتیب اجرای آن‌ها پشت سر هم و به شکلی کاملا قابل پیش‌بینی اتفاق می‌افتد. فرضا شبه کد زیر را در نظر بگیرید:

var a = 10;
var f = file.open("names.txt");
var names = f.read();
print names;
print a + 2;

ما انتظار انجام یک سری از وظایف را از کامپیوتر داشتیم. با توجه به آن انتظارات، به ترتیب خاصی کدهای‌مان را نگارش کردیم و از بالا به پایین دستوراتی که کامپیوتر باید اجرا کند را برایش شرح داده‌ایم و انتظار داریم که این دستورات به همان ترتیبی که نگارش شده‌اند اجرا شوند:

  • مقدار 10 در متغیر a ریخته شود.
  • فایل names.txt باز شود و ارجاعی در آن در متغیر f ریخته شود.
  • اطلاعات درون f خوانده شود و به طور رشته‌ای در متغیر s قرار بگیرد.
  • محتوایی که در s قرار دارد چاپ شود.
  • نتیجه‌ی محاسبه‌ای a + 2 نیز در خروجی چاپ شود.

در مثال بالا، ما به عنوان برنامه‌نویس چنین رویه‌ای را دنبال کردیم:

  • چیزی که از کامپیوتر انتظار داشتیم را در «فکر» خود حلاجی کردیم.
  • افکار خود را در قالب «کد» نگارش کردیم.
  • همچنین در این بین، «ترتیب نگارش» کدها را نیز مطابق افکار خود تنظیم کرده‌ایم.

کامپیوتر نیز اجرای کدهای ما را به شکلی قابل پیش بینی انجام خواهد داد. فرضا چاپ کردن محتوای فایل، حتما بعد از باز کردن فایل اتفاق می‌افتد.

ترکیب سه فاکتور «فکر»، «نگارش»، و «اجرا» مفهومی به اسم Control Flow یا «جریان کنترلی» را در برنامه‌نویسی به وجود می‌آورند که بیان کننده‌ی مدل اجرایی سیستم است.

در کدهای Synchronous، قضیه Control Flow ترتیبی به و شکلی قابل پیش‌بینی خواهد بود.

از همین رو، کدهای Synchronous کدهایی هستند که طبیعتی ترتیبی دارند. چیزی که در زبان انگلیسی می تواند با واژه‌ی Sequential بیان شود.

منظور از کدهای Asynchronous چیست

بر عکس چیزی که مردم فکر می کنند، مفهوم Asynchronous مشخصا در قطب مخالف Synchronous نیست! بلکه به نوعی مکمل آن است.

کدهای Asynchronous کدهایی هستند که در آن‌ها «ترتیب اجرای» فرامین، دقیقا با «ترتیب نگارش» آن‌ها مطابق نخواهد بود.

این ناهماهنگی چیز بدی نیست:

  • خیلی از مسايل در کامپیوتر طبیعت‌ای غیر ترتیبی دارند. نتیجه اجرای بسیاری از فرامین ممکن است در همان لحظه در دسترس قرار نگیرند. فرضا در مثال بالا، باز کردن فایل names.txt ممکن است ۱ میلی ثانیه طول بکشد، یا ممکن است ۱۰ ثانیه طول بکشد!
  • پردازنده‌های امروزی بسیار سریع‌تر از حدی هستند که بخواهیم از آن‌ها فقط برای اجرای «یک» روند ترتیبی بهره بگیریم. آن‌ها می توانند روند‌های اجرایی زیادی را به شکل غیر ترتیبی اجرا نمایند.

چون کدهای Asynchronous ذاتی غیر ترتیبی دارند، از جریان اجرایی پیشفرض کدها که به حالت ترتیبی اتفاق می‌افتد پیروی نمی‌کنند؛ در حقیقت، هنگام استفاده از مدل Asynchronous، هر بخش از کد می تواند دارای جریان اجرایی جداگانه‌ای باشد. به همین دلیل می توان به کمک اعمال Asynchronous به جای یک جریان اجرایی، چندین و چند جریان اجرایی داشت.

حالا می‌رسیم به قسمتی که در بیشتر نوشته‌ها باعث ریختن قیمه‌ها در ماست‌ها می‌شود! قسمتی که نویسنده مطلب تصمیم می‌گیرد مفهوم Asynchronous را با مدل پیاده‌سازی آن در زبان‌های مختلف قاطی کند، در حالی که Asynchronous یک مفهوم مستقل است که می توانند در هر سیستم به نوع متفاوتی ظاهر شود. اما چیزی که مهم است، غیر ترتیبی بودن کدهاست که در تمام پیاده سازی ‌ها مشترک است.

نحوه‌ی اجرا شدن کدهای Asynchronous ممکن است به طور موازی اتفاق بیفتند؛ که باعث اجرای Parallel خواهد شد. همچنین ممکن است به طور غیر موازی اتفاق بیفتد که باعث اجرای «همروند» یا Concurrent می‌شود.

زبان‌های مختلف، برای پیاده سازی مدل Asynchronous از راه‌حل‌های متفاوتی استفاده می‌کنند. در نهایت، موضوع به این برمی‌گردد که چگونه جریان‌های غیرترتیبی در مدل Asynchronous را اجرا و مدیریت می کنند:

  • با استفاده از Process ها، که مبتنی بر حافظه‌ی غیر اشتراکی هستند.
  • با استفاده از Thread ها، که بسیار نزدیک به Process ها هستند با این تفاوت که از حافظه‌ی اشتراکی استفاده می‌کنند.
  • با استفاده از Event ها، و به کمک Event loop و سایر الگوریتم‌هایی که مبتنی بر آن هستند (مثل callback یا Promise یا …)
  • با استفاده از ترکیبی از تمام موارد بالا… (یا هر مدل دیگری که ممکن است به فکرتان برسد)

هر کدام از روش‌های بالا مزایا و معایب خود را دارند. زبان‌هایی که به «همروند» بودن شهرت دارند، زبان‌هایی هستند که از تمام این مدل‌های استفاده کرده اند و سعی کرده‌اند به طور مناسبی هر مدل را در سناریو ای که برایش مناسب تر است استفاده کنند.

فرقی ندارد که از کدام زبان استفاده می‌کنید، و آن زبان از کدام مدل برای پیاده سازی مفهوم Asynchronous استفاده کرده است. در هر حال، هنگام کار با کدهای Asynchronous باید نکات زیر را بخاطر داشته باشید:

  • ترتیب اجرای کدهای مشخص نیست.
  • زمان به نتیجه رسیدن هر جریان اجرایی مشخص نیست.
  • نحوه‌ دسترسی هر جریان اجرایی به «داده‌ها» مشخص نیست.
  • کدهای Asynchronous بیانگر یک «مدل» اجرایی می‌باشند؛ Asynchronous بودن نه ربطی به سرعت کدها دارد و نه ضمانتی در اینباره ارايه می‌دهد.

بنابراین:

  • باید یک راه منطقی برای فکر کردن راجع به Control Flow کدهای‌تان پیدا کنید.
  • هیچوقت تصمیم گیری‌های‌تان را بر مبنای اینکه «اتمام فلان تکه از کد، فلان مقدار زمان خواهد برد» پایه گذاری نکنید.
  • خود را برای این قضیه که داده‌های شما به شکل همروند در دسترس خواهند بود آماده کنید.
  • کدهای شما ممکن است سریع‌تر شوند؛ و یا کندتر شوند! بدون فکر انتخابی انجام ندهید!

سخن آخر

در نوشته‌ای که خواندید، سعی شد بدون پرداختن و درگیر شدن با جزییات زبان‌های مختلف برنامه‌نویسی و ابزار‌های‌شان، توضیحی ساده از مفاهیم Synchronous و Asynchronous ارائه شود. همچنین از روی عمد، از واژه‌هایی مانند blocking و Non-blocking استفاده نشد تا باعث گمراهی بیشتر نشود؛ چرا که این مفاهیم داستان خودشان را دارند.

امیدوارم این نوشته هم برای خودتان مفید واقع شده باشد، و هم اینکه بتواند رفرنس خوبی برای‌تان باشد تا برنامه‌نویس های دیگری که قصد آشنایی با این مفاهیم دارند را به آن ارجاع دهید.

نظرات

comments powered by Disqus