Ответы SLaks и Killercam хороши; Я подумал, что просто добавлю немного больше контекста.
Ваш первый вопрос, по сути, касается того, какие методы можно пометить async
.
Метод, отмеченный как async
может возвращать void
, Task
или Task<T>
. В чем разница между ними?
Можно Task<T>
ожидать возврата асинхронного метода, и когда задача завершится, он предложит T.
Может Task
ожидаться возвращаемый асинхронный метод, и когда задача завершится, запланировано ее продолжение.
void
Возвращения метод асинхронной не может ожидаться; это метод «выстрелил и забыл». Он работает асинхронно, и вы не можете сказать, когда это будет сделано. Это более чем немного странно; как говорит SLaks, обычно вы делаете это только при создании асинхронного обработчика событий. Событие запускается, обработчик выполняет; никто не собирается «ждать» задачи, возвращаемой обработчиком событий, потому что обработчики событий не возвращают задачи, и даже если бы они это сделали, какой код будет использовать задачу для чего-то? Обычно это не код пользователя, который в первую очередь передает управление обработчику.
Ваш второй вопрос в комментарии по существу касается того, что можно await
редактировать:
Какие методы можно использовать await
? Можно ли редактировать метод, возвращающий пустоту await
?
Нет, нельзя ожидать метода возврата void. Компилятор преобразует await M()
в вызов M().GetAwaiter()
, где GetAwaiter
может быть метод экземпляра или метод расширения. Ожидаемое значение должно быть таким, для которого вы можете получить ожидающего; очевидно, что метод, возвращающий пустоту, не дает значения, из которого вы можете получить ожидающего.
Task
Методы возврата могут производить ожидаемые значения. Мы ожидаем, что третьи стороны захотят создать свои собственные реализации Task
-подобных объектов, которые можно ожидать, и вы сможете их ожидать. Однако вы не сможете объявлять async
методы, возвращающие что-либо, кроме void
, Task
или Task<T>
.
(ОБНОВЛЕНИЕ: мое последнее предложение может быть фальсифицировано будущей версией C #; есть предложение разрешить типы возврата, отличные от типов задач, для асинхронных методов.)
(ОБНОВЛЕНИЕ: упомянутая выше функция вошла в C # 7.)
async void
методы вызывают свое исключение для того,SynchronizationContext
что было активным в момент начала выполнения. Это похоже на поведение (синхронных) обработчиков событий. @DrewMarsh: параметрUnobservedTaskException
времени выполнения и применяется только к методам асинхронной задачи «выстрелил и забыл» , но не кasync void
методам.В случае, если звонящий хочет дождаться выполнения задачи или добавить продолжение.
Фактически, единственная причина для возврата
void
- это если вы не можете вернуться,Task
потому что вы пишете обработчик событий.источник
void
, у вас нет возможности добраться до задачи, которую он генерирует. (На самом деле, я не уверен, генерирует ли он вообще aTask
)Методы возвращаются
Task
иTask<T>
являются составными - это означает, что вы можете использоватьawait
их внутриasync
метода.async
Возвращаемые методыvoid
нельзя компоновать, но у них есть два других важных свойства:Второй момент важен, когда вы имеете дело с контекстом, который поддерживает количество невыполненных асинхронных операций.
Контекст ASP.NET - один из таких контекстов; если вы используете асинхронные
Task
методы, не ожидая их от асинхронногоvoid
метода, то запрос ASP.NET будет выполнен слишком рано.Другой контекст - это то, что
AsyncContext
я написал для модульного тестирования (доступно здесь ) -AsyncContext.Run
метод отслеживает количество невыполненных операций и возвращает его, когда оно равно нулю.источник
Тип
Task<T>
- это тип рабочей лошадки для параллельной библиотеки задач (TPL), он представляет собой концепцию «некоторой работы / задания, которая в будущем приведет к результату типаT
». Концепция «работа, которая будет завершена в будущем, но не вернет результата» представлена неуниверсальным типом Task.Как именно
T
должен быть получен результат типа, и детали реализации конкретной задачи; работа может быть передана другому процессу на локальной машине, другому потоку и т. д. Задачи TPL обычно передаются рабочим потокам из пула потоков в текущем процессе, но эта деталь реализации не является фундаментальной дляTask<T>
типа; скорее aTask<T>
может представлять любую операцию с высокой задержкой, которая создаетT
.На основании вашего комментария выше:
В
await
выражение означает «вычислить это выражение , чтобы получить объект , представляющий работу , что в будущем будет производить результат. Зарегистрироваться остаток текущего метода в качестве обратного вызова , связанного с продолжением этой задачи. После того, как эта задача производится и обратного вызова зарегистрирован, немедленно верните управление моему абоненту ". Это противоположно / в отличие от обычного вызова метода, что означает «запомните, что вы делаете, запустите этот метод, пока он не будет полностью завершен, а затем продолжите с того места, где вы остановились, теперь зная результат метода».Изменить: я должен процитировать статью Эрика Липперта в журнале MSDN за октябрь 2011 года, так как это было большим подспорьем для меня в понимании этого материала.
Дополнительную информацию и белые страницы можно найти здесь .
Я надеюсь, что это поможет.
источник