Если мой интерфейс должен вернуть Task, что является лучшим способом для реализации без операции?
В приведенном ниже коде, из-за интерфейса, класс LazyBar
должен вернуть задачу из этого метода (и для аргументов саке не может быть изменен). Если реализация LazyBar
необычна в том, что она выполняется быстро и синхронно - каков наилучший способ вернуть задачу No-Operation из метода?
Я пошел с Task.Delay(0)
ниже, однако мне хотелось бы знать, есть ли у него какие-либо побочные эффекты производительности, если функция называется много (для аргументов, скажем, сотни раз в секунду):
- Разве этот синтаксический сахар не ветрит чем-то большим?
- Запускает ли он пул потоков приложений?
- Достаточно ли расщепителя компилятора иметь дело с
Delay(0)
по-другому?
- Будет ли
return Task.Run(() => { });
быть другим?
Есть ли лучший способ?
using System.Threading.Tasks;
namespace MyAsyncTest
{
internal interface IFooFace
{
Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
}
/// <summary>
/// An implementation, that unlike most cases, will not have a long-running
/// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
/// </summary>
internal class LazyBar : IFooFace
{
#region IFooFace Members
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
// First, do something really quick
var x = 1;
// Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
// Is it a real no-op, or if I call this a lot, will it adversely affect the
// underlying thread-pool? Better way?
return Task.Delay(0);
// Any different?
// return Task.Run(() => { });
// If my task returned something, I would do:
// return Task.FromResult<int>(12345);
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
Test();
}
private static async void Test()
{
IFooFace foo = FactoryCreate();
await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
return;
}
private static IFooFace FactoryCreate()
{
return new LazyBar();
}
}
}
Ответы
Ответ 1
Использование Task.FromResult(0)
или Task.FromResult<object>(null)
будет нести накладные расходы, чем создание Task
с выражением no-op. При создании Task
с заранее заданным результатом не задействованы служебные данные планирования.
Сегодня я бы рекомендовал использовать Task.CompletedTask, чтобы выполнить это.
Ответ 2
Чтобы добавить к ответ Reed Copsey об использовании Task.FromResult
, вы можете повысить производительность еще больше, если кешируете уже выполненную задачу, поскольку все экземпляры завершенных задач - это то же самое:
public static class TaskExtensions
{
public static readonly Task CompletedTask = Task.FromResult(false);
}
С помощью TaskExtensions.CompletedTask
вы можете использовать один и тот же экземпляр во всем домене приложения.
последняя версия .Net Framework (v4.6) добавляет, что с Task.CompletedTask
статическое свойство
Task completedTask = Task.CompletedTask;
Ответ 3
Task.Delay(0)
как и в принятом ответе, был хорошим подходом, так как это кэшированная копия завершенного Task
.
Начиная с 4.6, теперь существует Task.CompletedTask
который является более явным по своему назначению, но не только Task.Delay(0)
прежнему возвращает один кэшированный экземпляр, он возвращает тот же единственный кэшированный экземпляр, что и Task.CompletedTask
.
Кэшируемая природа того и другого гарантированно останется постоянной, но в качестве зависящих от реализации оптимизаций, которые зависят только от реализации в качестве оптимизаций (то есть они все равно будут работать правильно, если реализация изменилась на что-то, что все еще действовало), использование Task.Delay(0)
была лучше принятого ответа.
Ответ 4
Недавно столкнулся с этим и продолжал получать предупреждения/ошибки о том, что метод недействителен.
Мы работаем над компилятором, и это очищает его:
public async Task MyVoidAsyncMethod()
{
await Task.CompletedTask;
}
Здесь собраны лучшие из всех советов, которые здесь представлены. Операция возврата не требуется, если вы фактически не делаете что-то в этом методе.
Ответ 5
Я предпочитаю решение Task completedTask = Task.CompletedTask;
.Net 4.6, но другой подход - отметить метод async и return void:
public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
}
Вы получите предупреждение (CS1998 - функция Async без выражения ожидания), но в этом контексте можно игнорировать.
Ответ 6
Когда вы должны вернуть указанный тип:
Task.FromResult<MyClass>(null);
Ответ 7
return Task.CompletedTask; // this will make the compiler happy