Немає ніякого потоку

Важлива правда про асинхронності в своїй первозданній формі: немає ніякого потоку.

Тих, хто заперечить, несть числа. «Ні», кричать вони, «якщо я очікую операцію, повинен бути потік, в якому виконується очікування! Можливо це потік з пулу. Або потік операційної системи! Або щось, пов'язане з драйвером пристрою...»

Не влемлем цим крикам. Якщо операція по-справжньому асинхронна, то ніякого потоку немає.

Скептики не переконані. Высмеем їх.

Прослідкуємо виконання асинхронної операції аж до заліза, приділивши особливу увагу платформі .NET і драйверу пристрою. Нам доведеться спростити опис, опустивши деякі деталі, але ми не відійдемо далеко від істини.

Розглянемо деяку операцію «записи» (файл мережевий потік, USB-тостер, куди завгодно). Наш простий код:

private async void Button_Click(object sender, RoutedEventArgs e)
{
byte[] data = ...
await myDevice.WriteAsync(data, 0, data.Length);
}


Ми вже знаємо, що UI потік не блокується під час очікування. Питання: чи існує інший потік, який приносить себе в жертву на Вівтарі Блокування, щоб UI потік міг жити?

Тримайтеся. Нам доведеться глибоко пірнути.

Перша зупинка: бібліотека (наприклад, код BCL). Ми припускаємо, що WriteAsync реалізований з використанням стандартної системи асинхронного вводу-виводу .NET фреймворку, яка заснована на overlapped I/O*. Т. о. запускається Win32 overlapped I/O операція із зазначенням ДЕСКРИПТОР пристрою.

ОС в свою чергу звертається до драйвера пристрою і просить його почати операцію запису. Ця прохання являє собою об'єкт, який описує операцію запису; такий об'єкт називається пакетом запиту вводу-виводу (I/O Request Packet, IRP).

Драйвер отримує IRP пакет і віддає пристрою команду почати запис даних. Якщо пристрій підтримує режим прямого доступу до пам'яті (DMA), то виконання команда полягає лише в запису адреси буфера в регістр пристрою. Це все, що може зробити драйвер; він позначає IRP пакет як «виконується» та повертає ОС.

image

Тут полягає суть: драйверу пристрою не дозволено блокувати управління під час обробки IRP пакет. Це означає, що якщо IRP пакет не може бути оброблений негайно, він повинен бути оброблений асинхронно. Це справедливо навіть для синхронних методів! На рівні драйвера пристрою всі (нетривіальні) запити асинхронно.

Цитуючи Томи Мудрості, «незалежно від типу I/O запиту, всі операції вводу-виводу, доручені драйверу додатком, виконуються асинхронно.»
З IRP пакетом у статусі «виконується» ОС повертається в бібліотеку, яка повертає незавершену завдання в оброблювач натискання кнопки, що в свою чергу зупиняє виконання методу, і UI потік продовжує своє виконання.

Ми рушили за запитом в саму безодню системи, до аж до фізичного пристрою.

Тепер операція запису в процесі виконання. Скільки потоків виконують її?

Анітрохи.

Ні потік драйвера пристрою, ні потік ОС, ні потік BCL, ні потік з пулу не виконують цю операцію запису. Немає ніякого потоку.

Тепер давайте простежимо за відповіддю, наступним з володінь демонів назад у світ смертних.

Через деякий час після початку операції пристрій завершує запис. І повідомляє про це процесор за допомогою переривання.

Викликається обробник переривання драйвера. Переривання є подією рівня процесора, тому будь-яка робота, якою був зайнятий процесор, тимчасово припиняється, незалежно від того, який потік в даний момент виконувався. Можна вважати, що обробник переривання «позичає» виконується потік, проте я дотримуюся думки, що обробники переривань виконуються на такому низькому рівні, що поняття «потоку» не існує; вони виконуються «під» всіма потоками, так сказати.

Якщо обробник переривань написаний правильно, то все, що він робить, це говорить пристрою «Спасибі за переривання» і ставить в чергу об'єкт відкладеного виклику процедури (Deferred Procedure Call, DPC).

Коли процесор закінчує обробляти переривання, він приступає до відкладеним викликів процедур. Вони також виконуються на такому низькому рівні, що говорить про «потоки» не зовсім коректно, як і обробники переривань, відкладені виклики процедур виконуються прямо на центральному процесорі, «під» системою управління потоками.

DPC бере IRP пакет, який являє собою запит на запис, і позначає його як «завершений». Однак цей статус існує тільки на рівні операційної системи; процес має власний адресний простір, і його теж треба повідомити. Тому ОС створює спеціальний-рівня-ядра об'єкт асинхронного виклику процедури (APC) і поміщає його в чергу того потоку, який володіє ДЕСКРИПТОРОМ.

Оскільки бібліотека/BCL використовує стандартний механізм overlapped I/O, то вона вже прив'язала дескриптор до порту вводу-виводу (I/O Completion Port), який є частиною пулу потоків. Тому для виконання APC використовується потік з пулу потоків введення-виведення**, який і повідомляє задачу про те, що вона завершена.

Оскільки завдання захопила UI контекст, то асинхронний метод відновлює своє виконання не в потоці пулу. Замість цього, продовження методу ставиться в чергу на виконання UI контексті, і UI контекст відновить виконання методу, коли добереться до нього.

Отже, ми бачимо, що поки операція виконувалася, не було ніякого потоку. Коли операція завершилася, різні потоки використовувалися для швидкого виконання різних завдань. Порядок виконання таких завдань — від мілісекунди (наприклад, виконання APC в потоці пулу) до мікросекунди (наприклад, обробка переривання). Але ніякої потік не був заблокований, очікуючи завершення операції.

image

Ланцюжок виконання, яку ми простежили, є «стандартною», в чомусь спрощеною. Існує незліченна кількість варіацій, однак суть залишається тією ж.

Ідея про те, що «десь має бути потік, що виконує асинхронну операцію», є помилковою.

Звільніть свій розум. Не намагайтеся знайти цей «асинхронний потік» — це неможливо. Замість цього, усвідомте істину:

Немає ніякого потоку.



* — однозначного перекладу на українську мову терміна «overlapped I/O» мені не зустрічалося. Близьким за змістом є термін «асинхронний ввід-вивід» (прим. пер.).
** — в CLR існує два пулу потоків: пул робочих потоків і пул потоків введення-виведення (прим. пер.).

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.