Какая разница между возвратом void и возвратом Задачи? - PullRequest
120 голосов
/ 08 ноября 2011

При просмотре различных C # асинхронных примеров CTP я вижу некоторые асинхронные функции, которые возвращают void, и другие, которые возвращают неуниверсальный Task.Я могу понять, почему возврат Task<MyType> полезен для возврата данных вызывающей стороне после завершения асинхронной операции, но функции, которые я видел, имеют тип возврата Task, никогда не возвращающий никаких данных.Почему бы не вернуть void?

Ответы [ 4 ]

200 голосов
/ 08 ноября 2011

SLaks и Killercam ответы хорошие;Я подумал, что просто добавлю немного контекста.

Ваш первый вопрос по существу о том, какие методы можно пометить async.

Метод, помеченный как async canвозврат void, Task или Task<T>.В чем различия между ними?

A Task<T> можно ожидать возвращения асинхронного метода, и когда задача завершится, она предложит T.

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

A void возвращение асинхронного метода не ожидается;это метод «огонь и забудь».Он работает асинхронно, и вы не можете сказать, когда это будет сделано.Это более чем немного странно;как говорит SLaks, обычно вы делаете это только при создании асинхронного обработчика событий.Событие срабатывает, обработчик выполняется;никто не собирается «ждать» задачи, возвращаемой обработчиком событий, потому что обработчики событий не возвращают задачи, и даже если они это сделают, какой код будет использовать задачу для чего-то?Обычно это не код пользователя, который передает управление обработчику.

Ваш второй вопрос, в комментарии, по сути о том, что может быть await ed:

Какие методы могут быть await ред?Может ли метод возврата void быть await ed?

Нет, метод возврата void не может ожидаться.Компилятор преобразует await M() в вызов M().GetAwaiter(), где GetAwaiter может быть методом экземпляра или методом расширения.Ожидаемое значение должно быть таким, за которое вы можете получить ожидающего;Очевидно, что метод, возвращающий пустоту, не дает значения, из которого вы можете получить ожидающего.

Task Методы возврата могут давать ожидаемые значения.Мы ожидаем, что третьи стороны захотят создать свои собственные реализации Task -подобных объектов, которые можно ожидать, и вы сможете их ожидать.Однако вам не разрешат объявлять async методы, которые возвращают что-либо кроме void, Task или Task<T>.

(ОБНОВЛЕНИЕ: моё последнее предложение может быть сфальсифицировано будущей версией C #; есть предложение разрешить типы возврата, отличные от типов задач, для асинхронных методов.)

(ОБНОВЛЕНИЕ:вышеупомянутая особенность сделала это в C # 7.)

22 голосов
/ 08 ноября 2011

В случае, если вызывающая сторона хочет дождаться выполнения задачи или добавить продолжение.

Фактически, единственная причина для возврата void - это если вы не можете вернуть Task, потому чтовы пишете обработчик событий.

18 голосов
/ 09 ноября 2011

Методы, возвращающие Task и Task<T>, являются составными - это означает, что вы можете await их внутри метода async.

async методы, возвращающие void, не являются компонуемыми, но у них есть два других важных свойства:

  1. Они могут использоваться в качестве обработчиков событий.
  2. Они представляют асинхронную операцию «верхнего уровня».

Второй момент важен, когда вы имеете дело с контекстом, который поддерживает count невыполненных асинхронных операций.

Контекст ASP.NET является одним из таких контекстов; если вы используете методы async Task, не ожидая их от метода async void, запрос ASP.NET будет выполнен слишком рано.

Другим контекстом является AsyncContext, который я написал для модульного тестирования (доступно здесь ) - метод AsyncContext.Run отслеживает количество ожидающих операций и возвращает его, когда он равен нулю.

12 голосов
/ 08 ноября 2011

Тип Task<T> - это тип рабочей лошадки параллельной библиотеки задач (TPL), он представляет концепцию «некоторой работы / работы, которая в будущем будет производить результат типа T». Концепция «работа, которая будет завершена в будущем, но не даст результата», представлена ​​неуниверсальным типом задачи.

Как именно будет получен результат типа T и детали реализации конкретной задачи; работа может быть передана другому процессу на локальном компьютере, другому потоку и т. д. Задачи TPL обычно передаются рабочим потокам из пула потоков в текущем процессе, но детали реализации не являются фундаментальными для Task<T> тип; скорее Task<T> может представлять любую операцию с высокой задержкой, которая производит T.

На основании вашего комментария выше:

Выражение await означает «вычислить это выражение, чтобы получить объект, представляющий работу, которая в будущем даст результат. Зарегистрируйте оставшуюся часть текущего метода в качестве обратного вызова, связанного с продолжением этой задачи. Как только эта задача и обратный вызов подписан, немедленно вернуть управление моему абоненту ". Это противоположно / в отличие от обычного вызова метода, что означает: «запомните, что вы делаете, запускайте этот метод, пока он не будет полностью завершен, а затем выберите, где вы остановились, теперь зная результат метода».


Редактировать: Я должен процитировать статью Эрика Липперта в журнале MSDN Magazine за октябрь 2011 года, так как это помогло мне понять этот материал.

Для получения дополнительной информации и страниц см. здесь .

Надеюсь, это поможет.

...