Важная истина об асинхронности в ее самой чистой форме: потока нет. Тех, кто возражает, бесчисленное множество.
«Нет, — кричат они, — если я жду операции, должен быть поток, на котором выполняется ожидание! Возможно это поток из пула.
Или ветка операционной системы! Или что-то связанное с драйвером устройства.
" Давайте не будем слушать эти крики.
Если операция действительно асинхронная, то потока нет. Скептики не убеждены.
Давайте посмеемся над ними.
Давайте проследим выполнение асинхронной операции вплоть до аппаратного обеспечения, уделив особое внимание платформе .
NET и драйверу устройства.
Нам придется упростить это описание, опустив некоторые детали, но мы не уйдем далеко от истины.
Давайте рассмотрим некоторую операцию «записи» (в файл, сетевой поток, USB-тостер, куда угодно).
Наш код прост:
Мы уже знаем, что поток пользовательского интерфейса не блокируется во время ожидания.private async void Button_Click(object sender, RoutedEventArgs e) { byte[] data = .
await myDevice.WriteAsync(data, 0, data.Length); }
Вопрос: Есть ли еще один поток, который жертвует собой на Алтаре блокировки, чтобы поток пользовательского интерфейса мог жить? Повесить там.
Нам придется нырнуть глубже.
Первая остановка: библиотека (например, код BCL).
Мы предполагаем, что WriteAsync реализован с использованием стандартной системы асинхронного ввода-вывода платформы .
NET, основанной на перекрывающемся вводе-выводе*.
Что.
Запускается перекрывающаяся операция ввода-вывода Win32 с указанием ОПИСАНИЯ устройства.
ОС, в свою очередь, связывается с драйвером устройства и просит его начать операцию записи.
Этот запрос представляет собой объект, описывающий операцию записи; такой объект называется пакетом запроса ввода-вывода (IRP).
Драйвер получает пакет IRP и дает указание устройству начать запись данных.
Если устройство поддерживает режим прямого доступа к памяти (DMA), то выполнение команды просто включает запись адреса буфера в регистр устройства.
Это все, что может сделать водитель; он помечает пакет IRP как «работающий» и возвращает управление ОС.
Вот в чем дело: драйверу устройства не разрешено блокировать управление во время обработки пакета IRP. Это означает, что если пакет IRP не может быть обработан немедленно, его необходимо обработать асинхронно.
Это верно даже для синхронных методов! На уровне драйвера устройства все (нетривиальные) запросы являются асинхронными.
Цитирование Том Мудрость , «Независимо от типа запроса ввода-вывода, все операции ввода-вывода, назначенные драйверу приложением, выполняются асинхронно».При IRP в статусе «работает» ОС возвращается в библиотеку, которая возвращает незавершенную задачу обработчику нажатия кнопки, который, в свою очередь, приостанавливает выполнение метода, а UI-поток продолжает его выполнение.
Мы проследили запрос в самые глубины системы, вплоть до физического устройства.
Сейчас выполняется операция записи.
Сколько потоков его выполняет? Нисколько.
Ни поток драйвера устройства, ни поток ОС, ни поток BCL, ни поток пула не выполняют эту операцию записи.
Нет потока.
Теперь давайте проследим за ответом из царств демонов обратно в мир смертных.
Через некоторое время после начала операции устройство прекращает запись.
И уведомляет об этом процессор с помощью прерывания.
Вызывается обработчик прерывания драйвера.
Прерывание — это событие на уровне процессора, поэтому любая работа, выполняемая процессором, временно приостанавливается, независимо от того, какой поток выполняется в данный момент. Можно было бы подумать об обработчике прерываний как о «заимствовании» работающего потока, но я придерживаюсь мнения, что обработчики прерываний выполняются на таком низком уровне, что понятия «поток» не существует; они выполняются, так сказать, «под» всеми потоками.
Если обработчик прерывания написан правильно, все, что он делает, это сообщает устройству «Спасибо за прерывание» и ставит в очередь объект отложенного вызова процедур (DPC).
Когда процессор завершает обработку прерываний, он начинает выполнять отложенные вызовы процедур.
К тому же они выполнены на столь низком уровне, что говорить о «потоках» не совсем корректно; Как и обработчики прерываний, вызовы отложенных процедур выполняются непосредственно на центральном процессоре, «под» системой управления потоками.
ЦОД принимает пакет IRP, представляющий запрос на запись, и помечает его как «завершенный».
Однако этот статус существует только на уровне операционной системы; процесс имеет собственное адресное пространство и также нуждается в уведомлении.
Поэтому ОС создает специальный объект асинхронного вызова процедур (APC) уровня ядра и помещает его в очередь потока, которому принадлежит HANDLER. Поскольку библиотека/BCL использует стандартный механизм перекрывающегося ввода-вывода, она уже привязала дескриптор к порту завершения ввода-вывода, который является частью пула потоков.
Поэтому для выполнения APC используется поток из пула потоков ввода-вывода**, который уведомляет задачу о ее завершении.
Поскольку задача захватила контекст пользовательского интерфейса, асинхронный метод возобновляет свое выполнение в потоке, отличном от потока пула.
Вместо этого продолжение метода ставится в очередь для выполнения в контексте пользовательского интерфейса, и контекст пользовательского интерфейса возобновит выполнение метода, когда доберется до него.
Итак, мы видим, что во время выполнения операции потока не было.
После завершения операции разные потоки использовались для быстрого выполнения разных задач.
Порядок выполнения таких задач варьируется от миллисекунд (например, выполнение APC в потоке пула) до микросекунд (например, обработка прерывания).
Но ни один поток не был заблокирован в ожидании завершения операции.
Цепочка выполнения, которую мы проследили, «стандартная», несколько упрощенная.
Вариаций бесчисленное множество, но суть остается той же.
Идея о том, что «где-то должен быть поток, выполняющий асинхронную операцию», ошибочна.
Освободите свой разум.
Не пытайтесь найти этот «асинхронный поток» — это невозможно.
Вместо этого осознайте истину: Нет потока.
* - Однозначного перевода на русский язык термина «перекрывающийся ввод-вывод» я не встретил.
Близким по смыслу является термин «асинхронный ввод-вывод» (прим.
).
** — в CLR есть два пула потоков: пул рабочих потоков и пул потоков ввода-вывода.
Теги: #асинхронный ввод-вывод #потоки #асинхронное программирование #async #перекрывающийся ввод-вывод #.
NET #разработка веб-сайтов #программирование #.
NET
-
Принципы Ит-Специалиста
19 Oct, 24 -
Охота На Космических Инспекторов
19 Oct, 24 -
Модульное Хранилище И Степени Свободы Jbod
19 Oct, 24