Най-добри практики в .Net асинхронното програмиране

Асинхронното програмиране ви позволява да извършвате ресурсно интензивни I / O операции, без да се налага да блокирате основната или изпълняващата нишка на приложението. Макар и полезен и на пръв поглед лесен за изпълнение, той идва с много сложност и рискове. Потенциалните рискове, свързани с асинхронното програмиране, особено използването на асинхронно програмиране по грешен начин, като не се спазват препоръчаните практики, включват блокировки, сривове на процесите и дори бавна производителност. Трябва също така да владеете писмено, отстранявайки грешки в асинхронния код.

Избягвайте да имате тип връщане void при асинхронни методи

Метод в C # се прави асинхронен метод, използвайки ключовата дума async в сигнатурата на метода. Можете да имате една или повече изчакващи ключови думи в асинхронен метод. Ключовата дума await се използва за обозначаване на точката на спиране. Един асинхронен метод в C # може да има всеки един от тези типове връщане: Task, Task и void. Ключовата дума "await" се използва в асинхронен метод, за да информира компилатора, че методът може да има точка на спиране и възобновяване.

Имайте предвид, че когато използвате TPL, еквивалентът на връщане на void в TPL е асинхронна задача. Трябва да знаете, че async void е и трябва да се използва само за асинхронни събития. Ако го използвате някъде другаде, ще срещнете грешки. С други думи, не се препоръчва метод за асинхронизация, който е анулиран като тип връщане. тъй като асинхронните методи, които връщат void, имат различна семантика, когато работите с изключения във вашето приложение.

Когато възникне изключение в асинхронен метод, който има връщащ тип Задача или Задача, обектът за изключение се съхранява в обекта Задача. Напротив, ако имате асинхронен метод с връщащ тип void, няма свързан обект Task. Такива изключения са повдигнати в SynchronizationContext, който е бил активен по времето, когато е бил извикан асинхронният метод. С други думи, не можете да обработвате изключения, създадени в асинхронния метод на void, използвайки обработчици на изключения, написани в асинхронния метод. Асинхронните методи, които имат връщащ тип void, също са трудни за тестване поради тази разлика в семантиката при обработка на грешки. За ваша информация класът SynchronizationContext в System.Threading пространството от имена представлява контекст на синхронизация в .Net и ви помага да поставите задача на опашка в друг контекст.

Следният списък с кодове илюстрира това. Имате два метода, а именно Test и TestAsync, а последният създава изключение.

public class AsyncDemo

   {

       public void Test()

       {

           try

           {

               TestAsync();

           }

           catch (Exception ex)

           {

               Console.WriteLine(ex.Message);

           }

       }

       private async void TestAsync()

       {

           throw new Exception("This is an error message");

       }

   }

Ето как можете да създадете екземпляр на класа AsyncDemo и да извикате метода Test.

static void Main(string[] args)

       {

           AsyncDemo obj = new AsyncDemo();

           obj.Test();

           Console.Read();

       }

Методът на теста прави извикване на метода TestAsync и повикването е обгърнато в блок try-catch с намерението да обработи изключението, хвърлено вътре в метода TestAsync. Обаче изключението, хвърлено вътре в метода TestAsync, никога няма да бъде уловено, т.е. обработено вътре в метода на повикващия Test.

Избягвайте смесване на асинхронен и синхронен код

Никога не трябва да имате комбинация от синхронен и асинхронен код. Лоша практика за програмиране е да блокирате асинхронния код, като извиквате Task.Wait или Task.Result. Бих препоръчал използването на асинхронен код от край до край - това е най-безопасният начин да се избегнат проникване на грешки.

Можете да избегнете блокировки, като използвате. ConfigureAwait(continueOnCapturedContext: false)всеки път, когато се обадите да чакате. Ако не използвате това, асинхронният метод ще блокира в точката, където е извикан await. В този случай просто информирате чакащия да не улавя текущия контекст. Бих казал, че е добра практика да използвате .ConfigureAwait (false), освен ако нямате конкретна причина да не го използвате.

Бих обсъдил повече за асинхронното програмиране в бъдещите ми публикации в блога тук. За повече информация относно най-добрите практики в асинхронното програмиране можете да се обърнете към страхотната статия на Стивън Клири в MSDN.