Я пытаюсь понять асинхронное ожидание в простейшей форме. Я хочу создать очень простой метод, который добавляет два числа ради этого примера, конечно, это совсем не время обработки, это просто вопрос формулировки примера здесь.
Пример 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
Пример 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
Если я DoWork1Async()
буду ждать, будет ли код выполняться синхронно или асинхронно?
Нужно ли обернуть код синхронизации, Task.Run
чтобы сделать метод ожидаемым И асинхронным, чтобы не блокировать поток пользовательского интерфейса?
Я пытаюсь выяснить, если мой метод - это Task
или возвращает Task<T>
, мне нужно обернуть код, Task.Run
чтобы сделать его асинхронным.
Глупый вопрос, я уверен, но я вижу в сети примеры, когда люди ждут кода, в котором нет ничего асинхронного и не заключенного в Task.Run
или StartNew
.
Ответы:
Во-первых, давайте проясним некоторую терминологию: «асинхронный» (
async
) означает, что он может вернуть управление вызывающему потоку до его запуска. Вasync
методе эти точки «доходности» являютсяawait
выражениями.Это очень отличается от термина «асинхронный», так как (неправильно) используется в документации MSDN годами для обозначения «выполняется в фоновом потоке».
Чтобы еще больше запутать проблему,
async
она сильно отличается от «ожидаемой»; Есть некоторыеasync
методы, чьи возвращаемые типы не являются ожидаемыми, и многие методы возвращают ожидаемые типы, которые не являются ожидаемымиasync
.Достаточно о том, чем они не являются ; вот что они есть :
async
Ключевое слово позволяет асинхронный метод (то есть, это позволяетawait
выражения).async
методы могут вернутьсяTask
,Task<T>
или (если необходимо)void
.Task
иTask<T>
.Итак, если мы сформулируем ваш вопрос следующим образом: «Как я могу выполнить операцию в фоновом потоке так, чтобы это было приемлемо», ответ будет такой
Task.Run
:(Но этот шаблон плохой подход; см. Ниже).
Но если ваш вопрос «как создать
async
метод, который может возвращать вызывающему объекту вместо блокировки», ответ заключается в том, чтобы объявить методasync
и использоватьawait
его «уступающие» точки:Итак, основной шаблон вещей заключается в том, чтобы
async
код зависел от «ожидаемых» в своихawait
выражениях. Этими «ожидаемыми» могут быть другиеasync
методы или просто обычные методы, возвращающие ожидаемые. Обычные методы, возвращающиеTask
/Task<T>
могут использоватьTask.Run
для выполнения кода в фоновом потоке, или (чаще) они могут использоватьTaskCompletionSource<T>
или один из его ярлыков (TaskFactory.FromAsync
,Task.FromResult
и т. Д.). Я не рекомендую оборачивать весь методTask.Run
; Синхронные методы должны иметь синхронные подписи, и потребитель должен решать, должен ли он быть заключен вTask.Run
:У меня есть
async
/await
вступление в моем блоге; в конце несколько хороших ресурсов для продолжения. Документы MSDN дляasync
тоже необычайно хороши.источник
async
методы должны вернутьсяTask
,Task<T>
илиvoid
.Task
иTask<T>
ожидаемо;void
не является.async void
сигнатура метода будет компилироваться, это просто ужасная идея, поскольку вы теряете указатель на асинхронную задачуvoid
это не ожидаемо.Task.Run
(какDoWorkAsync
в этом ответе). ИспользованиеTask.Run
для вызова метода из контекста пользовательского интерфейса является подходящим (какDoVariousThingsFromTheUIThreadAsync
).Task.Run
для вызова метода, но еслиTask.Run
вокруг всего метода (или почти весь) есть код, то это анти-паттерн - просто держите этот метод синхронным и перемещайтеTask.Run
его на уровень выше.Одна из самых важных вещей, которую нужно помнить при оформлении метода с помощью асинхронного, - это то, что по крайней мере есть один оператор ожидания внутри метода. В вашем примере я бы перевел это, как показано ниже, используя TaskCompletionSource .
источник
Когда вы используете Task.Run для запуска метода, Task получает поток из пула потоков для запуска этого метода. Таким образом, с точки зрения потока пользовательского интерфейса, он является «асинхронным», поскольку не блокирует поток пользовательского интерфейса. Это хорошо для настольных приложений, так как обычно вам не нужно много потоков для обеспечения взаимодействия с пользователем.
Однако для веб-приложения каждый запрос обслуживается потоком пула потоков, и, таким образом, количество активных запросов может быть увеличено путем сохранения таких потоков. Часто использование потоков пула потоков для имитации асинхронной операции не масштабируется для веб-приложений.
True Async не обязательно предполагает использование потока для операций ввода-вывода, таких как доступ к файлу / БД и т. Д. Вы можете прочитать это, чтобы понять, почему для операции ввода-вывода не нужны потоки. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
В вашем простом примере это чисто вычисление с привязкой к процессору, поэтому использование Task.Run вполне подойдет.
источник
I should NOT wrap the synchronous call in Task.Run()
это правильно. Если вы это сделаете, вы просто будете переключать темы. то есть вы разблокируете исходный поток запросов, но вы берете другой поток из пула потоков, который мог бы быть использован для обработки другого запроса. Единственный результат - это переключение контекста, когда вызов завершается с абсолютно нулевым коэффициентом усиления