Может быть, легче понять разницу, если мы забудем о Task.Run
на секунду. Разница между вашими 1-м и 2-м баллами в основном такая же, как ожидание или просто возвращение внутренней задачи из Task
метода возврата:
// 1)
public async Task WithAwait() => await SomeOtherTaskReturningMethod();
// 2)
public Task WithoutAwait() => return SomeOtherTaskReturningMethod();
Первый создает другую задачу, которая оборачивает внутреннюю задачу, что является небольшим накладным расходом, но в остальном эти два метода функционально почти идентичны. Небольшая разница в том, что когда SomeOtherTaskReturningMethod
выдает исключение, то внутренняя задача AggregatedException
будет развернута, и (первое) внутреннее исключение будет выдано дальше. Конечно, все еще зависит от того, что происходит с внешней задачей. В вашем третьем пункте также ожидается, что вы получите развернутое исключение из внешнего задания (если оно есть), в то время как 1-й и 2-й примеры просто запущены и забыты.
Теперь давайте снова рассмотрим Task.Run
. Главный вопрос, почему этот метод имеет перегрузки, которые принимают Task
с (фактически Task
возвращающие обратные вызовы), когда вы также можете просто вызвать await CheckVerification();
и получить тот же результат? (Я знаю, что это не было частью вопроса, но, возможно, стоит уточнить это)
Таким образом, причина таких Task.Run
перегрузок заключается в том, что сам метод возврата Task
не обязательно выполняется в другом потоке (я предлагаю это чтение ). Например, он может просто отправить сообщение через сетевой сокет и вернуть Task
, что будет завершено, когда будет получен конкретный ответ. Это делает операцию асинхронной, но не многопоточной.
Если у вас есть такая задача и вы хотите, чтобы она выполнялась в другом потоке, вы можете выполнить ее с помощью Task.Run
, который использует планировщик по умолчанию и в конечном итоге выполняет вашу задачу в потоке пула.
Стоит отметить, что это довольно редкий сценарий. В большинстве случаев вы должны полагаться на внутреннюю реализацию Task
возвращающих методов.
TL; DR:
Так что я думаю, что реальный вопрос заключается в том,
await CheckVerification();
или
await Task.Run(() => CheckVerification());
- лучшее решение (или, может быть, одно из них без await
, которое является режимом огня и забывания). В большинстве случаев я бы проголосовал за первый, но если вы действительно уверены, что задача, возвращаемая CheckVerification
, должна быть назначена потоку пула (независимо от того, делает ли он это внутри или нет), тогда второй вариант также может быть оправдан .