it-swarm.com.ru

Какая разница между возвратом void и возвратом Задачи?

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

120
James Cadd

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

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

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

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

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

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

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

Какие методы могут быть awaited? Может ли метод, возвращающий пустоту, быть awaited?

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

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

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

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

200
Eric Lippert

Если звонящий хочет подождать с заданием или добавить продолжение.

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

22
SLaks

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

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

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

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

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

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

18
Stephen Cleary

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

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

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

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


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

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

Я надеюсь, что это поможет.

12
MoonKnight