برنامه نویسی ناهمزمان با استفاده از Async / انتظار در C #

async و await کلمات کلیدی برای ساخت در C # معرفی شدند برنامه نویسی همزمان در بستر .NET راحت تر است. این کلمات کلیدی نحوه نوشتن کد را در بیشتر اکوسیستم C # تغییر اساسی داده اند. برنامه نویسی ناهمزمان به جریان اصلی تبدیل شده است و چارچوب های مدرن مانند ASP.NET Core کاملاً ناهمزمان هستند.

داشتن چنین تاثیری بر اکوسیستم C # ، برنامه نویسی ناهمزمان کاملاً ارزشمند است. اما در وهله اول برنامه نویسی ناهمزمان چیست؟

این مقاله قصد دارد به معرفی برنامه نویسی ناهمزمان ، نمایش استفاده از async و await کلمات کلیدی ، در مورد مشکل بن بست صحبت کنید و در پایان با برخی از نکات برای تجدید ساخت مسدود کردن کد C # با این کلمات کلیدی.

بیایید با اصطلاحات شروع کنیم.

همزمان مقابل موازی در مقابل ناهمزمان

تفاوت این سه اصطلاح چیست؟ همه آنها کاربردهای چند رشته ای هستند ، تعاریف آنها با هم همپوشانی دارند و اغلب به جای هم استفاده می شوند. به همین دلیل اصطلاحات پیاده سازی هایی که از استفاده از چند رشته استفاده می کنند می توانند گیج کننده باشند.

ما تفاوت های ظریف بین این اصطلاحات را مرور خواهیم کرد ، بنابراین می توانیم تعریف مشخصی را برای برنامه نویسی ناهمزمان ارائه دهیم.

بیایید یک برنامه GUI را به عنوان مثال در نظر بگیریم.

اعدام همزمان: انجام کارها پشت سر هم

کاربر یک دکمه را کلیک می کند و منتظر می ماند تا برنامه برنامه کنترل رویداد کلیک را به پایان برساند. از آنجا که فقط یک اتفاق می تواند در یک زمان رخ دهد ، UI تا زمان انجام کامل رویداد ، پاسخ نمی دهد. به همین ترتیب ، در حالی که رابط کاربر برای ورودی کاربر در دسترس است ، برنامه نمی تواند کاری در پس زمینه انجام دهد.

همزمان: انجام چندین کار به طور همزمان

کاربر یک دکمه را کلیک می کند ، و برنامه یک رشته جداگانه را در پس زمینه ایجاد می کند تا وظیفه مورد نیاز برای تأمین درخواست کاربر را انجام دهد همزمان. موضوعی که مسئولیت رسیدگی به رویدادهای UI را دارد ، بلافاصله پس از شروع موضوع پس زمینه ، با پاسخگو بودن UI ، دوباره در دسترس قرار می گیرد.

موازی: انجام چند نسخه همزمان از کاری

کاربر به برنامه دستور می دهد تا تمام پرونده های موجود در یک پوشه را پردازش کند. این برنامه با منطق پردازش تعدادی رشته را فعال می کند و پرونده ها را بین این رشته ها توزیع می کند.

ناهمزمان: مجبور نیستیم منتظر بمانیم تا یک کار قبل از شروع کار دیگر به پایان برسد

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

برنامه نویسی ناهمزمان

بر اساس اصطلاحات فوق ، می توانیم برنامه نویسی ناهمزمان را به صورت زیر تعریف کنیم:

رشته اجرا نباید منتظر باشد تا کار I / O یا CPU متصل شود.

به عنوان مثال می توان به دسترسی به سیستم فایل ، دسترسی DB یا درخواست HTTP اشاره کرد. نمونه هایی از عملیات متصل به CPU می تواند تغییر اندازه تصویر ، تبدیل یک سند یا رمزگذاری / رمزگشایی داده ها باشد.

فواید

استفاده از برنامه نویسی همزمان چند مزیت دارد:

  • جلوگیری از گرسنگی استخر نخ با “مکث” ؟؟ اجرای و آزاد کردن نخ به حوضچه نخ در طی فعالیتهای ناهمزمان
  • پاسخگو بودن رابط کاربر
  • عملکرد احتمالی سود حاصل از همزمانی

الگوهای برنامه نویسی ناهمزمان

.NET سه الگو برای انجام عملیات ناهمزمان فراهم می کند.

مدل برنامه نویسی ناهمزمان (APM): قانونمندی

همچنین به عنوان شناخته می شود IAsyncResult الگو ، با استفاده از دو روش اجرا می شود: BeginOperationName و EndOperationName.

public class MyClass { 
    public IAsyncResult BeginRead(byte [] buffer, int offset, int count, AsyncCallback callback, object state) {...};
    public int EndRead(IAsyncResult asyncResult);
} 

از اسناد مایکروسافت:

بعد از تماس BeginOperationName، یک برنامه می تواند اجرای دستورالعمل ها را در موضوع فراخوانی ادامه دهد در حالی که عملیات ناهمزمان روی یک موضوع دیگر انجام می شود. برای هر تماس به BeginOperationName، برنامه همچنین باید تماس بگیرد EndOperationName تا نتیجه عملیات را بدست آورید.

الگوی ناهمزمان مبتنی بر رویداد (EAP): قانونمندی

این الگو با نوشتن یک پیاده سازی می شود OperationNameAsync روش و OperationNameCompleted رویداد:

public class MyClass { 
    public void ReadAsync(byte [] buffer, int offset, int count) {...};
    public event ReadCompletedEventHandler ReadCompleted;
} 

عملیات ناهمزمان با روش async شروع می شود ، که باعث شروع می شود Completed رویدادی برای در دسترس قرار دادن نتیجه با اتمام عملیات async. یک کلاس که از EAP استفاده می کند نیز ممکن است حاوی یک باشد OperationNameAsyncCancel روش برای لغو یک عملیات ناهمزمان در حال انجام است.

ما فقط یک OperationNameAsync روشی که a را برمی گرداند Task یا یک ژنریک Task<T> هدف – شی:

public class MyClass { 
    public Task<int> ReadAsync(byte [] buffer, int offset, int count) {...};
} 

Task و Task<T> کلاسها عملیات ناهمزمان را در TAP مدل می کنند. درک آن مهم است Task و Task<T> کلاسهایی برای درک TAP ، که برای درک و استفاده از آن مهم است async/await کلمات کلیدی ، بنابراین بیایید در مورد این دو کلاس با جزئیات بیشتری صحبت کنیم.

Task and Task

Task و Task<T> کلاس ها هسته اصلی برنامه نویسی ناهمزمان در NET هستند. آنها انواع فعل و انفعالات را با عملکرد ناهمزمانی که نشان می دهند تسهیل می کنند ، مانند:

  • اضافه کردن وظایف ادامه
  • موضوع فعلی را مسدود کنید تا صبر کنید تا کار کامل شود
  • سیگنالینگ لغو (از طریق CancellationTokenد)

پس از شروع یک عملیات ناهمزمان و بدست آوردن Task یا Task<T> شی ، می توانید از رشته اجرای فعلی استفاده کنید تا همزمان سایر دستورالعمل هایی را که به نتیجه کار نیازی ندارند ، اجرا کنید یا در صورت لزوم با کار تعامل داشته باشید.

در اینجا چند کد به عنوان مثال آورده شده است که از وظایف برای تجسم آنچه در عمل نشان می دهد استفاده می کند:

using System;
using System.Threading.Tasks;

public class Example {
    public static void Main() {
       Task<DataType> getDataTask = Task.Factory.StartNew(() => { return GetData(); } );
       Task<ProcessedDataType> processDataTask = getDataTask.ContinueWith((data) => { return ProcessData(data);} );
       Task saveDataTask = processDataTask.ContinueWith((pData) => { SaveData(pData)} );
       Task<string> displayDataTask = processDataTask.ContinueWith((pData) => { return CreateDisplayString(pData); } );
       Console.WriteLine(displayDataTask.Result);
       saveDataTask.Wait();
    }
}

بیایید از طریق کد قدم بزنیم:

  • ما می خواهیم که برخی از داده ها را بدست آورید. ما استفاده می کنیم Task.Factory.StartNew() برای ایجاد یک کار که بلافاصله شروع به اجرا می کند. این کار اجرا می شود GetData() روش غیر همزمان و پس از اتمام ، داده ها را به آن اختصاص می دهد .Result ویژگی. ما این شی task وظیفه را به اختصاص می دهیم getDataTask متغیر.
  • ما می خواهیم که پردازش داده ها که GetData() روش فراهم خواهد کرد. صدا زدن .ContinueWith() روش ، ما غیر همزمان وظیفه دیگری ایجاد کنید و آن را به عنوان ادامه تنظیم کنید getDataTask. این کار دوم طول می کشد .Result از اولین کار به عنوان یک پارامتر ورودی (data) و تماس بگیرید ProcessData() روش با آن غیر همزمان. پس از اتمام ، آن را تعیین می کند داده پردازش شده به آن .Result ویژگی. ما این کار را به processDataTask متغیر. (توجه به این نکته مهم است که ، در حال حاضر ، ما نمی دانیم که آیا getDataTask تمام شده است یا نه ، و ما اهمیتی نمی دهیم. ما فقط می دانیم که چه اتفاقی می خواهد بیفتد و کد را برای آن می نویسیم.)
  • ما می خواهیم که داده های پردازش شده را ذخیره کنید. ما از همین روش برای ایجاد کار سوم استفاده خواهیم کرد SaveData() غیر همزمان وقتی پردازش داده به پایان رسید ، و آن را به عنوان ادامه تنظیم کنید processDataTask.
  • ما هم می خواهیم نمایش داده های پردازش شده. لازم نیست قبل از نمایش داده ها ذخیره شود ، بنابراین کار چهارمی ایجاد می کنیم که رشته نمایش را از داده های پردازش شده ایجاد می کند غیر همزمان وقتی پردازش داده به پایان رسید ، آن را تنظیم کنید همچنین به عنوان ادامه به processDataTask. (اکنون ما دو وظیفه داریم که به عنوان ادامه کار به آنها اختصاص داده می شود processDataTask. این کارها شروع می شود همزمان به محض processDataTask تکمیل شده است.)
  • ما می خواهیم که رشته نمایش را چاپ کنید به کنسول زنگ میزنیم Console.WriteLine() با .Result خاصیت displayDataTask. .Result دسترسی به اموال یک است مسدود کردن عمل؛ موضوع اعدام ما خواهد بود مسدود کردن تا زمان displayDataTask تکمیل شده است
  • ما می خواهیم که اطمینان حاصل کنید که داده ها ذخیره شده اند قبل از ترک Main() روش و خروج از برنامه. در این مرحله ، ما وضعیت کشور را نمی دانیم saveDataTask. ما تماس می گیریم .Wait() روش به مسدود کردن موضوع اعدام ما تا saveDataTask کامل می کند

تقریبا خوب

همانطور که در بالا نشان داده شد ، TAP و Task/Task<T> کلاسها برای استفاده از تکنیکهای برنامه نویسی ناهمزمان بسیار قدرتمند هستند. اما هنوز جای پیشرفت وجود دارد:

  • کد Boilerplate مورد نیاز برای استفاده از وظایف کاملاً گویا است.
  • تعیین تداوم و تصمیم گیری دقیق درباره اینکه کدام کار باید انجام شود ، بدان معنی است که بسیاری از جزئیات توسط برنامه نویس انجام می شود ، پیچیدگی را افزایش می دهد و کد را مستعد خطا می کند. (Verbosity ، همراه با افزایش پیچیدگی ، به معنی درک کد دشوار است ، بنابراین نگهداری آن دشوار است.)
  • با وجود این قدرت ، هیچ راهی برای صبر کردن برای انجام یک کار بدون مسدود کردن موضوع اجرا وجود ندارد.

این اشکال می تواند به چالش های مهمی برای تیم ها برای استفاده از TAP تبدیل شود.

این جایی است که async و await کلمات کلیدی وارد بازی می شوند.

ادامه مطالعه برنامه نویسی ناهمزمان با استفاده از Async / انتظار در C # در SitePoint.