Является ли мое понимание асинхронным/ожидающим, как это работает и его преимущества, правильно?
Я несколько раз утверждал свое понимание async/await, часто с некоторыми дебатами относительно того, насколько я прав. Я был бы очень признателен, если бы кто-нибудь мог подтвердить или опровергнуть мое понимание и прояснить любые заблуждения, чтобы я не распространял дезинформацию.
Высокоуровневое понимание
async
/await
- это способ избежать обратного вызова ада при написании асинхронного кода. Нить, выполняющая асинхронный метод, вернется в пул потоков, когда встретится с await
, и выполнит выполнение после завершения ожидаемой операции.
Низкоуровневое понимание
JIT разделит асинхронные методы на отдельные части вокруг точек await
, что позволит повторно войти в метод с сохранением состояния метода. Под обложками подразумевается какая-то государственная машина.
Отношение к Concurrency
async
/await
не подразумевает какой-либо concurrency. Приложение, написанное с использованием async
/await
, может быть полностью однопоточным, при этом все еще пожиная все преимущества, так же как и node.js, хотя и с обратными вызовами. В отличие от node.js,.NET многопоточен, поэтому, имея async
/await
, вы получаете преимущества неблокирующего ввода-вывода без использования обратных вызовов, а также с несколькими потоками выполнения.
Преимущества
async
/await
освобождает потоки, чтобы делать другие вещи, ожидая завершения ввода-вывода. Его также можно использовать в сочетании с TPL для работы с ЦП, связанной с несколькими потоками, или от потока пользовательского интерфейса.
Чтобы извлечь выгоду из неблокирующего IO, асинхронные методы должны быть построены поверх API, которые фактически используют неблокирующий IO, которые в конечном итоге предоставляются ОС.
Неправильное использование
Это самая большая точка зрения в моем понимании. Многие считают, что перенос операции блокировки в Task
и использование async
/await
приведет к увеличению производительности. Создав дополнительный поток для обработки операции, возвращая исходный поток в пул потоков, а затем возобновляя исходный метод после завершения задачи, все, что происходит, являются ненужными переключателями контекста, хотя и не освобождает потоки для выполнения другой работы. Хотя это не столько злоупотребление async
/await
, сколько и TPL, это мышление, похоже, связано с непониманием async
/await
.
Ответы
Ответ 1
Это очень правильно.
Несколько заметок:
- Нить, которая начинает выполнение асинхронного метода, является потоком вызывающего, который может или не может быть потоком
ThreadPool
.
- Если ожидание достигнуто, но ожидаемый (обычно
Task
) уже завершен, поток продолжит выполнение остальной части метода синхронно.
- Нить, которая возобновляет запуск метода, обычно представляет собой поток
ThreadPool
, но это зависит от SyncrhonizationContext
и TaskScheduler
.
- JIT здесь не участвует (не чаще обычного). Компилятор - это тот, который превращает асинхронный метод в конечный автомат. Вы можете видеть, что с этот пример TryRoslyn.
- Это правда, что async-await не обязательно подразумевает concurrency, поскольку он может быть однопоточным. Тем не менее, он может быть одновременно одновременно с одним потоком, одновременно запуская и ожидая нескольких асинхронных операций.
- async-wait и TPL не являются полностью отдельными частями. async-await построен поверх TPL. Именно поэтому он назвал Асинхронный шаблон на основе задач.
- Хотя наиболее асинхронными операциями являются операции ввода-вывода, не все они. Вы также обычно задерживаете асинхронно с
Task.Delay
или используете асинхронные конструкции синхронизации, такие как SemaphoreSlim.WaitAsync
.