Task.FromResult() vs. Task.Run()
В последнее время я столкнулся с довольно многими ситуациями, когда async
методы выполняются синхронно, но в любом случае возвращают задачу, поэтому их можно ожидать, например
public virtual Task CreateAsync(TUser user)
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException("user");
Context.Save(user);
Context.Flush();
return Task.FromResult(0);
}
Разумеется, лучше отправить вероятную долговременную операцию в поток и вернуть все еще активную задачу, чтобы ее можно было ожидать:
public virtual Task CreateAsync(TUser user)
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException("user");
return Task.Run(() =>
{
Context.Save(user);
Context.Flush();
});
}
Однако я подозреваю, что просто отключение потоков TPL - это не самая безопасная практика. Любой комментарий к этим двум различным шаблонам?
Ответы
Ответ 1
Если ваш метод синхронный, вы не должны возвращать Task
для начала. Просто создайте традиционный синхронный метод.
Если по какой-то причине это невозможно (например, вы реализуете некоторый асинхронный интерфейс), возвращающий завершенную задачу с помощью Task.FromResult
или даже лучше в этом случае Task.CompletedTask
(добавленный в.NET 4.6) намного лучше, чем использование Task.Run
in реализация:
public virtual Task CreateAsync(TUser user)
{
// ...
return Task.CompletedTask;
}
Если потребитель вашего API сильно обеспокоен тем, что метод Task
-returning не работает синхронно, он может использовать Task.Run
чтобы убедиться.
Вы должны иметь в виду, что асинхронные методы могут иметь значительную синхронную часть (часть до первого ожидания), даже если они в конечном итоге продолжаются асинхронно. Вы не можете предположить, что методы async возвращают Task
немедленно.
Ответ 2
Task.FromResult
фактически не создает или не запускает задачу, а просто переносит возвращаемый результат в объект задачи. Я лично использовал его в Unit Tests
где мне нужно моделировать методы Async
и, конечно, я бы не захотел запускать реальные задачи в модульных тестах.
Кроме того, Task.Run
фактически создаст задачу и запустит задачу TaskScheduler. Не рекомендуется использовать Task.Run
когда вы выполняете Async
программирование. Скорее используйте await
задач. Смотрите, что некоторые делают и не выполняют задачи Стивена Клири.