Насправді, асинхронізація / очікування не така вже й магічна. Повна тема досить широка, але для швидкого, але досить повного відповіді на ваше запитання, я думаю, ми можемо впоратися.
Давайте розглянемо просту подію натискання кнопки у додатку Windows Forms:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
Я зараз явно не буду говорити про те, що воно GetSomethingAsync
повертається. Скажімо, це щось, що завершиться через, скажімо, 2 секунди.
У традиційному, несинхронному світі обробник подій натискання кнопки виглядатиме приблизно так:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
Після натискання кнопки у формі програма з’явиться, що заморозиться приблизно на 2 секунди, поки ми будемо чекати завершення цього методу. Що відбувається, так це те, що "насос повідомлення", в основному цикл, блокується.
Цей цикл постійно запитує вікна "Хто-небудь щось робив, як-небудь перемістив мишу, натиснув щось? Чи потрібно щось перефарбовувати? Якщо так, скажіть мені!" а потім обробляє те, що "щось". Цей цикл отримав повідомлення про те, що користувач натиснув "button1" (або еквівалентний тип повідомлення від Windows), і в кінцевому підсумку зателефонував наш button1_Click
метод вище. Поки цей метод не повернеться, ця петля тепер застрягла в очікуванні. Це займає 2 секунди, і протягом цього часу жодне повідомлення не обробляється.
Більшість речей, які мають справу з Windows, виконуються за допомогою повідомлень, а це означає, що якщо цикл повідомлень перестає перекачувати повідомлення, навіть на секунду, це швидко помічається користувачем. Наприклад, якщо перемістити блокнот чи будь-яку іншу програму поверх власної програми, а потім знову відійти, у вашу програму надсилається шквал повідомлень про фарбу, які вказують, яка область вікна, яка зараз раптом знову стала видимою. Якщо цикл повідомлень, який обробляє ці повідомлення, чекає чогось, заблокованого, фарбування не робиться.
Отже, якщо в першому прикладі async/await
не створюються нові теми, то як це робити?
Що ж, це те, що ваш метод розділений на два. Це одна з таких широких тематичних речей, тому я не буду надмірно деталізуватись, але достатньо сказати, що метод розділений на ці дві речі:
- Весь код, що веде до
await
, включаючи дзвінок доGetSomethingAsync
- Весь код наступний
await
Ілюстрація:
code... code... code... await X(); ... code... code... code...
Впорядковано:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
В основному метод виконується так:
- Він виконує все до
await
Він викликає GetSomethingAsync
метод, який робить свою справу, і повертає щось, що завершиться за 2 секунди в майбутньому
Поки що ми все ще знаходимось у початковому виклику до кнопки1_Click, що відбувається в основному потоці, викликаному з циклу повідомлень. Якщо код, який веде до await
потрібного часу, займає багато часу, інтерфейс користувача все одно застигне. У нашому прикладі не так багато
Що await
ключове слово, разом з яким - то розумним магії компілятора, робить те , що це в основному що - щось на зразок «Добре, ви знаєте , що я збираюся просто повернутися з клацання кнопки обробника подій тут. Якщо ви (як, речей ми» знову чекаю) обійдіть завершення, повідомте мене, тому що у мене ще є якийсь код, який потрібно виконати ".
Насправді це дозволить класу SynchronizationContext знати, що це робиться, що, залежно від реального контексту синхронізації, який зараз відтворюється, буде черговим для виконання. Контекстний клас, який використовується в програмі Windows Forms, буде ставити його в чергу, використовуючи чергу, яку перекачує цикл повідомлень.
Таким чином, він повертається до циклу повідомлень, який тепер може продовжувати перекачувати повідомлення, як-от переміщення вікна, зміна розміру або натискання інших кнопок.
Для користувача, інтерфейс користувача знову реагує, обробляє інші натискання кнопок, змінює розмір і найголовніше - перемальовує , тому, схоже, він не замерзає.
- Через 2 секунди річ, на яку ми чекаємо, завершується, і те, що відбувається зараз, - це те, що (ну, контекст синхронізації) розміщується повідомлення в черзі, на яке дивиться цикл повідомлень, кажучи: "Ей, я отримав ще код для Ви повинні виконати ", і цей код є всім кодом після очікування.
- Коли цикл повідомлень потрапить до цього повідомлення, він, в основному, "знову введе" цей метод там, де він припинився, відразу після
await
і продовжуючи виконувати решту методу. Зауважте, що цей код знову викликається з циклу повідомлень, тому, якщо цей код буде робити щось тривале без async/await
належного використання , він знову блокує цикл повідомлень
Є багато рухомих частин під капотом тут , так що тут деякі посилання на додаткову інформацію, я збирався сказати «вам це потрібно», але ця тема є досить широким , і це досить важливо знати деякі з цих рухомих частин . Незмінно ви зрозумієте, що асинхронізація / очікування все ще є пропускною концепцією. Деякі основні обмеження та проблеми все ще просочуються до оточуючого коду, і якщо вони не виконуються, вам, як правило, доведеться налагоджувати програму, яка розбивається випадковим чином, здавалося б, без вагомих причин.
Гаразд, що робити, якщо GetSomethingAsync
закручується нитка, яка завершиться за 2 секунди? Так, тоді, очевидно, є нова нитка в грі. Ця нитка, однак, не тому , що в асинхронної-ності цього методу, це відбувається тому , що програміст цього методу вибрав нитка для реалізації асинхронного коду. Майже всі асинхронні введення / виведення не використовують нитку, вони використовують різні речі. async/await
самі по собі не розкручують нові теми, але очевидно, що "речі, яких ми чекаємо" можуть бути реалізовані за допомогою потоків.
У .NET є багато речей, які не обов'язково обертають нитку самостійно, але все ще залишаються асинхронними:
- Веб-запити (та багато інших речей, пов’язаних із мережею, що потребує часу)
- Асинхронне читання та запис файлів
- та багато іншого, хороша ознака - якщо у розглянутого класу / інтерфейсу є методи, названі
SomethingSomethingAsync
або BeginSomething
і, EndSomething
і якщо вони IAsyncResult
задіяні.
Зазвичай для цих речей не використовується нитка під кришкою.
Гаразд, значить, ви хочете деякі з цих "широких тем теми"?
Що ж, давайте запитаємо Спробуйте Рослін про наше натискання кнопки:
Спробуйте Рослін
Я не збираюся посилатися на повний генерований клас тут, але це дуже гарні речі.