Выполняется ли асинхронный код в потоке пользовательского интерфейса или в новом / другом потоке, чтобы не блокировать пользовательский интерфейс? - PullRequest
0 голосов
/ 26 мая 2020

Хотя может показаться, что этот вопрос задавали несколько раз, и я уже получил высоко оцененные ответы, я хотел бы предположить, что несколько ответов противоречат друг другу, и я никогда не смогу прийти к полному пониманию внутреннего устройства асинхронного кода. Я полностью понимаю, что это означает продолжение последовательного выполнения кода и выполнение задачи позже, я пытаюсь понять, что позже часть.

Ответ 1 - Предлагает UI / Main

Во-первых, этот вопрос содержал внутри себя следующий тест, предполагающий, что асинхронный код выполняется в потоке Main / Ui и ссылается на статью объясняет, почему нет другого потока для асинхронного кода.

Q: «На мой взгляд, поскольку я в основном занимаюсь разработкой пользовательского интерфейса, asyn c код - это код, который не запускается в пользовательском интерфейсе нить, но в какой-то другой нити ".

A: Это убеждение распространено, но ложно. Не требуется, чтобы асинхронный код выполнялся в каком-либо втором потоке.

Фактически, ответ предполагает, что асинхронное программирование с потоками «неправильно», говоря: «Потоки являются рабочими. Могут происходить асинхронные рабочие процессы. все в одном потоке. Смысл асинхронного рабочего процесса состоит в том, чтобы избежать найма большего количества рабочих, если вы можете этого избежать ».

Ответ 2 - Предлагает UI / Main

Следующий вопрос, который я прочитал, также предполагает, что асинхронный код выполняется в потоке Main / UI, , однако используемое сравнение равно JavaScript, что, как мы все знаем, является однопоточный язык. Например, предположим, что я запускаю этот код,

 function wait(ms) {
  var start = Date.now(),
      now = start;
  while (now - start < ms) {
    now = Date.now();
  }
}
setTimeout(() => {
    wait(5000); 
}, 3000)

setTimeout будет вызываться асинхронно, а после 3000ms обратный вызов будет добавлен к Event Loop и в конечном итоге будет выполнен. Однако выполнение метода будет выполняться в потоке Main / UI и, таким образом, в результате пользовательский интерфейс будет заморожен для 5000ms.

Ответ 3 - Предложение нового потока

Этот ответ предполагает, что асинхронный код запускается в новый поток внутри ответа, который он говорит. «Когда вы выполняете что-то асинхронно, вы можете перейти к другой задаче до ее завершения. При этом в контексте компьютеров это означает выполнение процесса или задачи в другом« потоке ».

Ответ 4 - Предложение новой темы

Окончательный ответ предполагает, что асинхронный код основан на потоке, говоря: " В общем случае асинхронный вызов не обязательно создает новый поток. Это один из способов его реализации, другие способы - это уже существующий пул потоков или внешний процесс. Это сильно зависит от языка, объектной модели (если есть) и среды выполнения. Асинхронность просто означает, что вызывающий поток не сидит и не ждет ответа, и при этом асинхронная активность не происходит в вызывающем потоке. «, причем основной точкой является », и при этом асинхронная активность не происходит в вызывающем потоке. thread "

Я не уверен, куда go с этого момента, насколько я понимаю, я думаю, что асинхронный код должен выполняться в другом потоке, чтобы никогда заблокировать пользовательский интерфейс , за исключением JavaScript, я полагаю. Однако даже когда дело доходит до выполнения асинхронного кода на однопоточном языке, я бы подумал, что любой выполняемый обратный вызов функции должен должен быть выполнен до завершения, прежде чем будет запущен другой обратный вызов.

Второй ответ здесь предполагает рисование, когда асинхронный код выполняется в одном потоке, он останавливается и запускает другой асинхронный обратный вызов, переключаться вперед и назад, как поток, пока они не закончатся. Для меня это не означает, что Смысл в том, что с асинхронным кодом они обычно включают обратный вызов для предотвращения любого вида состояния гонки, предположим, что B требовал, чтобы A сначала выполнялся до завершения, B будет обратным вызовом внутри A. Обратные вызовы A и B не будут «работать вместе» и переключаться между собой.

1 Ответ

1 голос
/ 26 мая 2020

Давайте работать с «низкого уровня».

Для оборудования низкого уровня большинство современных устройств (сеть, диск) используют мастеринг шины или DMA для передачи в / из основной памяти компьютера и генерации IRQ когда перевод завершен; и поддерживает модель «драйвер выдает команду устройству, затем устройство выдает IRQ по завершении команды» для вещей, которые не связаны с вводом-выводом. Для примера; если вы хотите (например) асинхронно прочитать некоторые данные с диска на однопроцессорном компьютере; ЦП (драйвер устройства) может сообщить аппаратному обеспечению контроллера диска, что делать, затем ЦП может продолжать выполнять полезную работу, пока данные передаются контроллером диска, а затем, когда поступает IRQ (сигнализирующий о завершении передачи), он может запускать какое-то действие «асинхронная передача завершена».

Если устройство не поддерживает это (например, должно использовать какой-то «запрограммированный ввод-вывод», когда для передачи данных должен использоваться ЦП и т. д. c ), то асинхронность нужно было бы каким-то образом эмулировать - используя другой ЦП, лгая (выполняя это синхронно и делая вид, что "асинхронное завершение" произошло немедленно), или используя один ЦП, который быстро переключается между разными задачами (тот, который запрашивал асинхронная операция и другая, выполняющая асинхронную операцию), чтобы создать иллюзию асинхронности.

Следующий уровень - это ядро ​​операционной системы и драйверы устройств. Практически для всех современных операционных систем все, о чем нужно уделять достаточно времени, является (внутренне) полностью асинхронным (даже если базовое оборудование не поддерживает его, асинхронные операции эмулируются); в первую очередь из-за множественной обработки и производительности (например, обеспечение того, чтобы отдельные запросы от отдельных процессов могли заставить все аппаратные устройства выполнять полезную работу параллельно, если / где это возможно, часто с приоритетами ввода-вывода, чтобы попытаться обеспечить выполнение более важной работы раньше, чем неважная работа , часто в сочетании с различными стратегиями предварительной выборки, происходящими в фоновом режиме, чтобы увеличить вероятность того, что работа будет выполнена до того, как ее запросит какой-либо процесс). Тем не мение; API, предоставляемый пользовательскому пространству, может не предоставлять поддержку ядром асинхронных операций процессам пользовательского пространства. Например, изначально POSIX API вообще не поддерживал асинхронные операции (он был активно взломан после того, как было слишком поздно, поэтому вы до сих пор не можете делать базовые c вещи, такие как асинхронное открытие файла. ).

Следующий уровень - это среда исполнения языка. Это заклинило между стандартом языка (который может обслуживать или не обслуживать асинхронные операции и может иметь или не иметь какой-то «потоки / волокна / что угодно в пользовательском пространстве») и API ядра (который может предоставлять или не обеспечивать некоторые / любые асинхронные операции). В случаях, когда среда выполнения языка поддерживает асинхронные операции, а базовый API ядра - нет, его нужно каким-то образом эмулировать - например, используя (один, несколько?) «Потоков ядра» (и, возможно, скрывая это за любым «пользователем» -space threading / fibres / something "), или ложью (выполняя это синхронно и делая вид, что" асинхронное завершение "произошло немедленно).

Последний уровень - сторонние библиотеки / модули / пакеты для добавления асинхронных операций на вершина языка, который изначально их не поддерживал. Это всегда предполагает какую-то эмуляцию.

Обратите внимание, что если вы посмотрите на весь набор уровней, система в целом может эмулировать асинхронные операции на двух разных уровнях (в ядре / в драйвере устройства, плюс в пользовательском пространстве во время выполнения языка или поверх него); поэтому одновременно можно использовать 2 разных метода имитации асинхронности (и даже в этом случае ни один из них не может использовать потоки / задачи для имитации / обеспечения асинхронности).

Конечно, если есть 100 языков и в среднем 4 реализации каждого языка; тогда будет 400 перестановок, которые могут иметь разные ответы на то, как на самом деле реализуются асинхронные операции; поэтому вы можете ожидать (потенциально) разных ответов для C#, Javascript, ... которые могут быть правильными (для этой реализации этого языка), несмотря на то, что они неверны (для других реализаций этого языка или других языков).

...