نخ های ناهمگام پس زمینه در سی شارپ | به زبان ساده
اگر میخواهید در زبان #C یک درخواست وب ارسال کنید و یا هر کار دیگری در پسزمینه انجام دهید باید از «وظایف پسزمینه ناهمگام» (asynchronous background tasks) استفاده کنید تا نخ اصلی برنامه مسدود نشود. در این مقاله با مفهوم نخ های ناهمگام پس زمینه در سی شارپ و شیوه استفاده از آنها شنا خواهیم شد.
Async/Await چیست؟
برای استفاده از Task-ها ابتدا باید مفهوم Async/Await را درک کنید. وظایف سی شارپ لزومی به اجرای ناهمگام ندارند، اما با توجه به هدف منفردشان که بازنمایی یک عملیات ناهمگام است، غالباً به صورت ناهمگام اجرا میشوند. بدیهی است که ما نمیخواهیم کارهایی مانند واکشی درخواستهای وب یا نوشتن روی هارددرایو را روی نخ اصلی اجرا کنیم، زیرا موجب از کار افتادن بقیه بخشهای اپلیکیشن از جمله رابط کاربری در زمان انتشار برای دریافت نتیجه میشود.
async/await یک ساختار خاص برای اجرای عملیات ناهمگام است. اگر تابعی به صورت async نشانهگذاری شود، معمولاً یک Task بازگشت میدهد مگر این که دستگیرههای رویدادی باشد که مقدار void بازگشت دهد.
درون یک تابع ناهمگام میتوان از کلیدواژه await جهت انتظار برای پایان گرفتن عملیات ناهمگام بدون مسدودسازی کل نخ استفاده کرد. هر چیزی که پس از کلیدواژه await بیابد تنها پس از پایان گرفتن عملیات await-شده اجرا خواهد شد.
1public async Task FetchWebResponse(string url)
2{
3 var response = await SendRequest(url)
4}
مقداری که await میشود باید یک Task باشد چون این دو با همکاری یکدیگر عمل میکنند. زمانی که تابع ()SendRequest را فراخوانی میکنید، یک <Task<T بازگشت میدهد و برنامه منتظر میماند تا وظیفه پایان یابد. Await را میتوان به عنوان کلیدواژه مورد استفاده برای بازگشت یا انتظار برای مقدار یک task تصور کرد.
Task چیست؟
وظیفه یا Task در واقع پوششی است که برای کار با تابعهای ناهمگام مورد استفاده قرار میگیرد. آنها در واقع نماینده مقداری هستند که در آینده بازگشت خواهد یافت. از کلیدواژه await میتوان جهت انتظار برای پایان گرفتن یک وظیفه استفاده کرد. همچنین میتوانیم با بررسی Task.IsCompleted و سپس خواندن مقدار Task.Result مستقیماً به آن دسترسی پیدا کنیم.
برای ساخت Task باید یک تابع async با نوع بازگشتی Task<T> بنویسیم. سپس تنها کاری که باید بکنیم این است که یک مقدار از نوع T بازگشت دهیم تا NET. آن را به صورت یک وظیفه تفسیر کند. ما میتوانید از await درون این task جهت انتظار برای عملیات async استفاده کنید که به نوبه خود یک task بازگشت میدهد.
برای غاز اجرای یک وظیفه میتوانید از Task.Run استفاده کنید. به این ترتیب وظیفه در استخر نخ صفبندی میشود که در پسزمینه روی یک نخ متفاوت اجرا میشود. «استخر نخ» (thread pool) یک صف از وظایف دریافت میکند و آنها را به نخهای CPU برای پردازش انتساب میدهد. زمانی که آنها بازگشت یابند، آنها را در یک لیست از وظایف تکمیلشده قرار میدهد که مقادیرشان میتوانند خوانده شوند.
با این حال، علیرغم این که این یک نخ پسزمینه است، همچنان برای استفاده از async/await کاملاً مهم است. اگر یک فراخوانی مسدودساز به یک API روی نخ پسزمینه صورت گیرد و آن را await نکنید، NET. تا زمان تکمیل این فراخوانیان نخ را مسدود نگه میدارد. به این ترتیب استخر نخ با نخهای بیاستفاده پر میشود که هیچ کاری به جز تخریب عملکرد انجام نمیدهند.
نخ UI
اگر لازم است که یک وظیفه را از نخ UI به صورت await دربیاورید، باید آن را با Task.Run آغاز کنید و سپس به طور مرتب بررسی کنید که آیا وظیفه تکمیل شده است یا نه. اگر چنین شده باشد باید مقدار را مدیریت کنید.
همچنین میتوانید وظایف را درون وظایف دیگر اجرا و await کنید. برای نمونه فرض کنید یک تابع درون یک وظیفه به نام ()DoExpensiveCalculation دارید که اجزای آن مدت زمانی طول میکشد. به جای پردازش کردن به صورت همگام میتوانید آن را به صورت یک Task بنویسید و در نخ پسزمینه در ابتدای وظیفه اصلی صفبندی کنید. سپس زمانی که نیاز به مقدار این محاسبه باشد میتوانید وظیفه را await کنید تا زمانی که وظیفه تکمیل نمیشود صبر کند و مقدار را بازگشت دهد.