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