Web Devs
641 subscribers
218 photos
22 videos
17 files
233 links
Articles, News, Jokes, Quotes, Back-End and UI/UX for web developers.
Github : https://github.com/fullStackDevsGroup
Advertising: @adsfullStackDevs
Download Telegram
#AsynchronousProgramming
#Async #Await
#Tasks

✳️ Async/Await on a Separate Thread

🔸 اجرای عملیات های long-running به صورت غیر همزمان (asynchronous) در سی شارپ با mark کردن آنها با استفاده از کلمه کلیدی های async/await انجام میشود.
🔹 متد هایی که یک Task را به عنوان خروجی باز میگردانند با استفاده از این کلمه کلیدی ها به صورت غیر همزمان اجرا میشوند.
درواقع از await میتوان به همراه یک awaitable argument استفاده کرد که یک عملیات asynchronous است.
دو نوع رایج awaitable argument ها در دات نت ،
Task , Task<T>
میباشد.
🔹 اجرای متدهای long-running به صورت همزمان (synchronous)، باعث بلاک شدن thread جاری تا زمان اتمام آن متد میشود.
🔹همچنین در اپلیکیشن هایی که رابط گرافیکی کاربری دارید اجرای متد های long-running به صورت همزمان، سبب بلاک شدن thread اجرا کننده رابط گرافیکی (UI) میشود و تا زمان اتمام اجرای آن متد رابط کاربری به صورت فریز شده باقی میماند.
اما چنین متدی که خروجی آن یک Task نیست و نمیتوان آنرا با استفاده از async و await به صورت asynchronous اجرا کرد، را چگونه به یک متد asynchronous تبدیل کنیم تا در یک thread جدا اجرا شود.
اینجاست که Task.Run به کمک ما می آید.
🔸 متد Run از کلاس Task باعث اجرا شدن کد های صف شده در یک thread جدا میشود.
معمولا این thread جدید ازthread pool می آید .
▫️ در واقع thread pool مجموعه ای از worker thread هایی است که توسط .net برای اجرای اپلیکیشن، مدیریت میشوند.
▫️ هم چنین یک worker thread یک thread ای است که به درخواست یک client برای انجام کاری فعال میشود.
🔹 مهمترین نکته این که Task.Run به عنوان خروجی یک Task باز میگرداند که این بدین معنی است که می توانید آنرا await کنید.
متد Task.Run دارای overload های متفاوتی است که به عنوان پارامتر یک Action delegate یا یک Func delegate دریافت میکند.
🔸 تاکنون دانستیم که صدا زدن متد Task.Run برای اجرای یک متد long-running باعث اجرا شدن این متد بروی یک thread جدید در thread pool میشود اما پس از اتمام اجرای این متد long-running بقیه و ادامه کد ها بروی کدامین thread اجرا خواهد شد⁉️
🔸 در اکثر مواقع می خواهیم تا ادامه کار بروی thread اصلی ادامه پیدا کند مخصوصا زمانی که اپلیکیشن دارای یک رابط کاربری گرافیکی میباشد و میخواهیم بعد از انجام شدن آن متد long-running عناصری را بروی رابط گرافیکی در thread اصلی آپدیت نماییم. برای آنجام این کار کافیست Task.Run را await نمایید زیرا زمانی که که یک Task را await می کنید اطلاعات context جاری از SynchronizationContext گرفته میشود تا دوباره به thread اصلی اپلیکیشن باز گردد.
🔻زمانی که روی یک متد ای که به صورت Async اجرا میشود ConfigureAwait(false) قرار میدهید. بازگشت به context اصلی صورت نمیگیرد.

🔹 اما SynchronizationContext چیست و کار آن چه میباشد؟
🔸 هنگامی که با یک اپلیکیشن UI based کار میکنید وابستگی و ارتباط بین thread ها مسئله بسیار مهمی است
زمانی که با چنین اپلیکیشن هایی کار میکنید محدودیتی که وجود دارد این است که عناصر و کنترلر های صفحه توسط UI thread ساخته شده یا تغییر و آپدیت شوند و اگر نیاز باشد که به عنوان مثال 10 تغییر بروی عناصر صفحه صورت گیرد، این عملیات ها در صف قرار میگیرند تا به صورت متوالی توسط UI thread انجام شوند.
این محدودیت برای کمک به UI framework جهت جلوگیری از پیچیدگی های هماهنگی و مدیریت بین thread های مختلف برای آپدیت عناصر و کنترلر های صفحه صورت گرفته است.
و SynchronizationContext ارتباط با UI thread را به صورت منظم مرتب میکند.
در Asp.net Core اما SynchronizationContext برایر null میباشد.

✳️ درا این قسمت یک overview در مورد Task.Run پرداختیم تا در قسمت بعدی به عملیات های I/O و CPU bound پرداخته و با مفاهیم Asynchronous بیشتر آشنا میشویم.


@FullStackDevs
#IOBound
#CPU_Bound
#AsynchronousProgramming
#Async #Await

✳️ CPU vs. I/O-Bound Code

🔸 برای تشخیص اینکه چه زمانی از Task.Run استفاده کنیم. ابتدا نیاز به دانستن تفاوت بین کد های CPU-bound و I/O-bound داریم.

🔹 CPU-Bound
🔸 وقتی کدی CPU-Bound باشد اساسا این منظور را میرساند که پردازنده کامپیوتر یا به طور خاص thread ای در پردازنده، جایی است که به واسطه اجرای آن کد دچار ترافیک و صرف زمان زیاد خواهد شد.
▫️ در زمان اجرای کد های CPU-Bound پردازنده تا جای ممکن با سرعت به اجرای چنین کدهایی میپردازد اما با این وجود باز هم مدت زمانی که صرف اجرای چنین کدهایی خواهد شد قابل توجه خواهد بود. به عبارت دیگر در اجرای چنین کدهایی پردازنده محلی است که شما را از اجرای سریعتر باز میدارد و میتوانیم بگوییم که اجرای این کد ها باعث delay در پردازنده خواهد شد.

🔹 I/O-Bound Code
🔸 در مقابل در کد های I/O-Bound نرخ انتقال داده در دستگاه های ورودی و یا خروجی و یا عملیات های ورودی و خروجی عاملی است که باعث ایجاد delay خواهد شد.
مانند خواندن یا ذخیره کردن داده ها از یک فایل یا برقراری ارتباط با سرور جهت دانلود یا آپلود و ...
▫️ در همه این عملیات ها پردازنده تا حدودی بیکار است به این خاطر که منتظر اتمام ارسال و دریافت داده ها میباشد. در صورتی که عملیات I/O-Bound یک ارتباط با یک سرور خارجی باشد و به هر دلیلی اتصال به درستی برقرار نشود احتمالا پردازنده کاملا بیکار خواهد بود تا زمانی که ارتباط برقرار شود.

🔸 همانطور که متوجه شدید گرچه در هر دو عملیات های CPU vs. I/O-Bound تاخیر (delay) وجود دارد اما این عملیات ها در ذات کاملا متفاوت هستند.

✳️ Nature of Asynchronous operation

اصولا استفاده از Task,Run برای اجرای یک عملیات در thread ای جدا، برای عملیات های CPU Bound مسمر ثمر است، ام نه برای عملیات های I/O Bound. ولی چرا ⁉️

🔸 اکثر کامپیوترهای امروزی چندین هسته دارند که کمی شبیه این است که انگار چندین پردازنده دارند. زمانی که اپلیکیشن شروع به اجرا شدن میکند دسته ای از thread ها توسط .Net runtime ساحته میشود تا اپلیکیشن از آنها استفاده کند.
تعداد این thread ها به تعداد هسته های پردازنده کامپیوتر مرتبط است
در نتیجه اگر thread اصلی اپلیکیشن مشغول باشد می توانیم با استفاده از Task.Run عملیات سنگین خود را به لطف پردازنده چند هسته ای مان در thread ای جدا اجرا کنیم .

🔻توجه داشته باشید که thread و core گرچه مرتبط هستند اما با هم تفاوت دارند .
یک thread یک مفهوم نرم افزاری است و یک core یک ابزار سخت افزاری .
▫️ این امکان وجود دارد تا هر چند thread ای که میخواهید بسازید اما تعداد core ها ثابت است .

🔸 وقتی Task.Run را صدا میزنید یک thread از thread pool (اگر در دسترس باشد) اجرا خواهد شد و این امر بهبود عملکردی را برای اپلیکیشن به ارمغان می آورد.

🔸 وقتی که یک عملیات I/O Bound را با استفاده از Task.Run اجرا میکنید بر روی یک thread از thread pool (اگر در دسترس باشد) اجرا خواهد شد ولی در اون thread هم باز باید برای دریافت داده ها، منتظر اتمام عملیات I/O باشد.
اینکار ممکن است روشی سریع و آسان برای پاسخگو نگه داشتن اپلیکیشن برای انجام فرامین کاربران باشد اما راه کارآمدی برای استفاده هر چه بهتر منابع سیستم نیست.

🔸 در چنین عملیات هایی راه جل خیلی بهتر استفاده از async و
await است. در این روش باز هم اپلیکیشن پاسخو خواهد بود .

🔸 به منظور استفاده از
await برای عملیات های I/O Bound بدون استفاده کردن از Task.Run لازم است که از یک متد asynchronous استفاده کنید که یک Task را return کند. خود این متد نباید از Task.Run استفاده کرده باشد .
انجام این کار زمانی که دارید با کلاس های از پیش ساخته شده .Net نظیر FileStream
و HttpClient کار میکنید، ساده است زیرا این کلاس ها برای اعمال خود متد های asynchronous را در اختیار مان قرار میدهند.
گاهی ممکن است کلاسی در درون .net یا یک لایبری فقط متد های synchronous در اختیار مان قرار دهد که در این موارد ممکن مجبور شوید تا از Task.Run استفاده کنید.
اما همیشه و تا جای ممکن از async و
await به جای Task.Run برای عملیات های I/O Bound استفاده کنید.

@FullStackDevs