Является ли мое понимание асинхронным / ожидающим, как оно работает и его преимущества, правильно?

17

Я несколько раз утверждал свое понимание async / await, часто с некоторыми дебатами относительно того, насколько я прав. Я был бы очень признателен, если бы кто-нибудь мог подтвердить или опровергнуть мое понимание и прояснить любые заблуждения, чтобы я не распространял дезинформацию.

Высокоуровневое понимание

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

Низкоуровневое понимание

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

Отношение к параллелизму

async / await не подразумевает какой-либо параллелизм. Приложение, написанное с использованием async / await , может быть полностью однопоточным, при этом все еще пожиная все преимущества, так же, как и node.js, хотя и с обратными вызовами. В отличие от node.js, .NET является многопоточным, поэтому, имея async / await , вы получаете преимущества неблокирующего ввода-вывода без использования обратных вызовов, а также с несколькими потоками выполнения.

Преимущества

async / await освобождает потоки, чтобы делать другие вещи, ожидая завершения ввода-вывода. Его также можно использовать в сочетании с TPL , чтобы сделать CPU связанная работа над несколькими потоками или от потока пользовательского интерфейса.

Чтобы использовать неблокирующий IO, асинхронные методы должны быть построены поверх API, чтобы фактически использовать неблокирующие IO, которые в конечном итоге предоставляются ОС.

Неправильное использование

Это самая большая точка зрения в моем понимании. Многие считают, что завершение операции блокировки в Task и использование async / await приведет к увеличению производительности. Создав дополнительный поток для обработки операции, возвращая исходный поток в пул потоков, а затем возобновляя исходный метод после завершения задачи, все, что происходит, - это ненужные переключатели контекста, хотя и не освобождает потоки для выполнения другой работы. Хотя это не столько злоупотребление async / await , сколько и TPL, это мышление, похоже, связано с непониманием async / await .

    
задан w.brian 09.08.2015 в 01:00
источник
  • Ваше понимание идеально, 100% правильно. –  Deepak Bhatia 09.08.2015 в 01:15
  • Это компилятор, а не джиттер, который создает конечный автомат; нет такой вещи, как ждать на уровне ниже. Вы можете изучить классы, созданные, если вы используете ILSpy или аналогичные. –  Jon Hanna 09.08.2015 в 01:35
  • О вашем неправильном использовании: если в вашей нити нечего делать, тогда вы должны позволить своему потоку. Buf, если в вашем потоке есть другие значимые вещи, например, чтобы ваш пользовательский интерфейс реагировал, тогда разумно выполнять тяжелую работу в отдельной задаче –  Harald Coppoolse 10.08.2015 в 12:24

1 ответ

13

Это очень правильно.

Несколько заметок:

  • Нить, которая начинает выполнение асинхронного метода, является потоком вызывающего, который может или не может быть потоком ThreadPool .
  • Если ожидание достигнуто, но ожидаемый (обычно Task ) уже завершен, поток продолжит выполнение остальной части метода синхронно.
  • Поток, который возобновляет запуск метода, обычно является ThreadPool thread, но это зависит от SyncrhonizationContext и TaskScheduler .
  • JIT здесь не участвует (не чаще обычного). Компилятор - это тот, который превращает асинхронный метод в конечный автомат. Вы можете видеть, что с этого TryRoslyn примером .
  • Это правда, что асинхронный ожидания не обязательно подразумевает параллелизм, поскольку он может быть однопоточным. Тем не менее, он может быть одновременно одновременно с одним потоком, одновременно запуская и ожидая нескольких асинхронных операций.
  • async-wait и TPL не являются полностью отдельными частями. async-await построен поверх TPL. Вот почему он называется асинхронным шаблоном на основе задач.
  • Хотя наиболее асинхронными операциями являются операции ввода-вывода, не все они. Вы также обычно задерживаете асинхронно с Task.Delay или используете асинхронные конструкции синхронизации, такие как SemaphoreSlim.WaitAsync .
ответ дан i3arnon 09.08.2015 в 01:13
  • Ничего себе, отличный ответ. Не могли бы вы прояснить одно? «он может быть одновременно одновременно с одним потоком, одновременно запуская и ожидая нескольких асинхронных операций». Как это возможно? Существуют ли потоки, создаваемые за пределами среды выполнения .NET? –  w.brian 09.08.2015 в 02:12
  • @ w.brian «одновременный» здесь не означает многопоточность. Это означает, что одновременно выполняются несколько операций (то есть одновременно). Теперь, если ваши операции асинхронны и поэтому не требуют потока, вы можете выполнять параллельные операции только с одним потоком. stackoverflow.com/a/10495458/885318 –  i3arnon 09.08.2015 в 02:17
  • @ w.brian параллелизм одного потока находится на уровне оборудования ввода-вывода - один поток может запускать (и ждать) несколько операций ввода-вывода, в то время как в синхронном режиме ему придется ждать (при блокировке ) для каждого из них до конца, прежде чем он сможет запустить следующий. –  shay__ 09.08.2015 в 07:58