В чем разница между возвратом пустоты и возвратом задачи?

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

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

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

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

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

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

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

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

Какие методы можно await ? Может ли ожидаемый метод возврата пустоты?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Основываясь на вашем комментарии выше:

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


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

Для нагрузок больше информации и белых страниц см. Здесь .

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

  • Выполнение задачи в фоновом режиме в приложении WPF
  • Выполнение задач параллельно
  • ConfigureAwait подталкивает продолжение в stream пула
  • Запуск двух асинхронных задач параллельно и сбор результатов в .NET 4.5
  • Очередь процесса с многопоточным или задачами
  • Шаблон для самоотдачи и перезапуска задачи
  • Отмена ожидающей задачи синхронно в streamе пользовательского интерфейса
  • Давайте будем гением компьютера.