При просмотре различных образцов С# Async CTP я вижу некоторые асинхронные функции, возвращающие void
, и другие, которые возвращают не общий Task
. Я вижу, почему возвращение Task<MyType>
полезно возвращать данные вызывающему, когда операция async завершается, но функции, которые я видел, которые имеют тип возврата Task
, никогда не возвращают никаких данных. Почему бы не вернуться void
?
Какая разница между возвращением пустоты и возвратом задачи?
Ответ 1
Ответы SLaks и Killercam хорошие; Я думал, что просто добавлю немного больше контекста.
Ваш первый вопрос в основном о том, какие методы могут быть отмечены async
.
Метод, помеченный как
async
, может возвращатьvoid
,Task
илиTask<T>
. Каковы различия между ними?
A Task<T>
может быть возвращен возвращающий метод async, и когда задача завершится, он предложит T.
A Task
возвращаемый метод async может быть ожидаемым, и когда задача завершается, планируется запуск задачи.
A void
возвращающий асинхронный метод не может быть ожидаемым; это метод "огонь и забыть". Он работает асинхронно, и вы не можете сказать, когда это будет сделано. Это более чем немного странно; как говорит SLaks, обычно вы делаете это только при создании асинхронного обработчика событий. Событие срабатывает, выполняется обработчик; никто не собирается "ждать" задачи, возвращенной обработчиком событий, потому что обработчики событий не возвращают задачи, и даже если бы они и делали, какой код использовал бы что-то? Обычно это не код пользователя, который в первую очередь передает управление обработчику.
Второй вопрос, в комментарии, в основном о том, что может быть await
ed:
Какие методы могут быть
await
ed? Может ли метод возврата void бытьawait
ed?
Нет, метод ожидания возврата не может быть ожидаемым. Компилятор переводит await M()
в вызов M().GetAwaiter()
, где GetAwaiter
может быть методом экземпляра или методом расширения. Ожидаемое значение должно быть таким, за которое вы можете получить awaiter; очевидно, что метод возврата void не дает значения, из которого вы можете получить awaiter.
Task
-повторяющие методы могут давать ожидаемые значения. Мы ожидаем, что третьи стороны захотят создать свои собственные реализации Task
-подобных объектов, которые можно ожидать, и вы сможете их подождать. Однако вам не будет разрешено объявлять методы async
, которые возвращают что-либо, кроме void
, Task
или Task<T>
.
(UPDATE: мое последнее предложение может быть сфальсифицировано будущей версией С#, есть предложение разрешить типы возвращаемого типа, кроме типов задач для методов async.)
(ОБНОВЛЕНИЕ: упомянутая выше функция сделала это на С# 7.)
Ответ 2
В случае, если вызывающий абонент хочет ждать выполнения задачи или добавить продолжение.
На самом деле единственной причиной возврата void
является то, что вы не можете вернуть Task
, потому что вы пишете обработчик событий.
Ответ 3
Возвращаемые методы Task
и Task<T>
являются составными - это означает, что вы можете await
их внутри метода async
.
async
методы, возвращающие void
, не являются составными, но у них есть еще два важных свойства:
- Они могут использоваться как обработчики событий.
- Они представляют собой асинхронную операцию "верхнего уровня".
Вторая точка важна, когда вы имеете дело с контекстом, который поддерживает количество выдающихся асинхронных операций.
Контекст ASP.NET - один из таких контекстов; если вы используете методы async Task
, не ожидая их из метода async void
, запрос ASP.NET будет завершен слишком рано.
Другой контекст - это AsyncContext
, который я написал для модульного тестирования (доступно здесь) - метод AsyncContext.Run
отслеживает количество выдающихся операций и возвращается, когда он равен нулю.
Ответ 4
Тип Task<T>
- тип рабочей лошади параллельной библиотеки задач (TPL), он представляет собой концепцию "некоторая работа/задание, которая в будущем будет производить результат типа T
". Концепция "работа, которая будет завершена в будущем, но не возвращает результат", представлена не-общим типом задачи.
Точно так же, как будет создан результат типа T
, и деталь реализации конкретной задачи; работа может быть обработана другим процессом на локальном компьютере, другим потоком и т.д. Задачи TPL обычно обрабатываются рабочими потоками из пула потоков в текущем процессе, но эта деталь реализации не является фундаментальной для Task<T>
тип; скорее, Task<T>
может представлять любую операцию с высокой задержкой, которая создает T
.
Основываясь на вашем комментарии выше:
Выражение await
означает: "Оцените это выражение, чтобы получить объект, представляющий работу, которая в будущем приведет к результату. Подпишите оставшуюся часть текущего метода как обратный вызов, связанный с продолжением этой задачи. и обратный вызов зарегистрирован, немедленно вернуть управление моему абоненту". Это противоположно/в отличие от обычного вызова метода, что означает "помните, что вы делаете, запускайте этот метод до тех пор, пока он не будет полностью завершен, а затем заберите, где вы остановились, теперь зная результат метода".
Изменить: я должен привести статью Эрика Липперта в октябре 2011 года в журнале MSDN Magazine, поскольку это очень помогло мне в понимании этого материала в первую очередь.
Для нагрузок больше информации и белых страниц см. здесь.
Надеюсь, это поможет.