برنامه‌نویسی Async

در دنیای وب استفاده از Async و Sync در جای مناسب خود، بسیار اهمیت دارد و مخصوصا که با توجه به بالا رفتن حجم تراکنش‌ها و داده‌ها و البته متعاقب آن، هزینه‌های سرور، هر چه سعی کنیم به‌نوعی سربار را کم کنیم یا از ظرفیت‌های موجود به نحو صحیح‌تری استفاده کنیم، سرویس‌دهی بهتری را برای مشتریانمان به ارمغان خواهیم آورد.



خیلی اوقات عملیات ما I/O Bound است (کار با دیتابیس). یعنی یک عمل به‌دلیل اینکه منتظر شبکه یا خواندن هارد است، منتظر میماند و عملا از تمام توان CPU استفاده نمیشود. خاصیت async کردن اینست زمانیکه علیاتی I/O باند داریم، میتوانیم به رکئوست‌ها بیشتری پاسخ دهیم.


  • استفاده از async الزاما، سرعت را سریعتر نمیکند. اگر روی یک پردازنده‌ی تک‌هسته‌ای باشیم، عملا به‌دلیل جابجایی زیاد بین Threadها، کندتر هم خواهد شد. اما اگر پردازنده چند هسته‌ای باشد، ممکن است سیستم‌عامل تشخیص دهد برخی از عملیات را در Threadهای جداگانه انجام دهد. اما اگر بخاهیم مجبور کنیم که سیستم‌عامل از چند هسته استفاده کند، بایستی از برنامه‌نویسی موازی استفاده کنیم.
  • برنامه‌نویسی concurrent، یعنی عملیات رو به چند تکه تقسیم کنیم و آنهارا با هم پیش ببریم. اینجوری همه‌ی عملیات با هم انجام میشود و حس رضایت بهتری دارد.
  • برنامه نویسی parallel، انجام کل عملیات با هم هست. و اندازه کل عملیات به اندازه بیشترین زیرعملیات است. مشکل آن اینست که اگر وابستگی بین زیرعملیات وجود داشته باشد، ممکن است خطاساز باشد. یا مثلا یک عمل یکسان، در آن واحد توسط چند نفر انجام میشود (مثلا خرید آخرین محصول از انبار)… و بنابراین سعی میکنیم که زیاد از برنامه‌نویسی موازی استفاده نکنیم.




  • ترد پول ThreadPool، در دات نت تعداد زیادی ترد خالی رزرو در نظر گرفته میشود، هر موقع رکوئستی اومد سمت iis یا kestrel، یک ترد به آنها تخصیص داده میشود. دو نوع CLR ThreadPool داریم، یکی worker که برای عملیات ریاضی است که cpu bound هستند و دیگری I/O competion port زمانیکه از async استفاده میکنیم، از این نوع ترد استفاده میکند.
  • در وب توصیه میشود از متدهای Async استفاده کنید. معمولا پیاده سازی متدهای Async پیچیده تر است و کد نویسی آن زمان بیشتری می برد. اما در این روش منابع سرور به سرعت آزاد میشود و می تواند به Request های بیشتری پاسخ دهد. مخصوصا زمانی که با فایل یا دیتابیس کار می کنید.
  • در بیشتر مواقع Race Condition در برنامه نویسی Async رو میدهد. در این حالت دستورات بلافاصله اجرا نمیشوند بلکه سیستم عامل زمان اجرای آنها را مشخص میکند. شما فقط می توانید رویدادی دریافت کنید که پایان یافتن اجرای متد را گزارش میدهد.
  • ابزار bombardier ابزاری برای ایجاد تعداد دلخواهی Connection  و request 
  • برای تغییر هر اکشنی از sync به async کافی هست که async را قرار دهیم که خروجی آن باید Task<T> باشد را اضافه کنیم و در مواردی که میخاهیم عملیاتی را بصورت async اجرا کنیم پشت‌اش await میگذاریم. که بایستی از نسخه‌ی async آن عملیات استفاده شود

await _context.SaveChangesAsync();

  • وقتی بخشی بصورت async پیاده کنیم باید برای استفاده از آن نیز async استفاده کرد.همچنین برای sync. نباید همزمان از دو روش استفاده کرد. مثلا اینکه بخواهیم دستورات Async را با .wait() بصورت سینک اجرا کنیم، ترد مربوطه را قفل میکند و منتظر میشه خروجی async بدست بیاید.
  • استفاده از wait میتواند بسیار تاثیر منفی در پرفورمنس بگذارد چرا که دو ترد اشغال شده است و ظرفیت را نصف میکند.
  • همچنین استفاده سینک در متدهای Async اشتباه است.
  • یکی از روش‌های بهینه برای اجرای Async اینه که بصورت concurrent آنرا اجرا کنیم. بدین صورتکه بجای اینکه پشت هر عملی آسکرونی await بگذاریم آنرا جمع کنیم و یکجا اجرا کنیم

var result = await Task.WhenAll(UserTask,BookTask);

  • کلمه کلیدی await منظورش این هست که در هر خط RunTime باید صبر کند تا زمانیکه کار اون عملیات تمام شود تا به خط بعدی برود
  • در Task.WhenAll منتظر میماند تا همه‌ی خروجی‌ها تولید شوند. که نتایج را در یک آرایه میرزد و برای دسترسی به نتایج آن result[0] برای متغییر اول و الی‌آخر…
  • امکان استفاده از گزینه‌ Task.WhenAny وجود دارد. که به‌محض اینکه یکی از خروجی‌ها تولید شدند، تکمیل میشود.
  • در سی‌شارپ اگر اکسپشنی رخ دهد بصورت حبابی به آنکه صدایش زده برگشت داده میشود. اگر async void داشته باشیم و خطا رخ دهد چون برنامه دارد در ترد دیگری اجرا میشود بایستی جهت اطلاع به لایه بالاترش بگوید که خطا رخ داده که چون void است دسترسی ندارد و ارتباط قطع میشود. برای همین هیچگاه نباید از این استفاده کرد و باید async task استفاده نمود.