Если мой интерфейс должен вернуть задачу, каков наилучший способ иметь реализацию без операции?

в коде ниже, из-за интерфейса, класс LazyBar должен возвращать задачу из ее метода (и для аргументов не может быть изменен). Если s необычна тем, что она выполняется быстро и синхронно - каков наилучший способ вернуть задачу без операции из метода?

Я пошел с Task.Delay(0) ниже, однако я хотел бы знать, имеет ли это какие-либо побочные эффекты производительности, если функция называется много (для Аргументов ради, скажем, сотни раз в секунду):

  • этот синтаксический сахар un-ветер к чему-то большому?
  • он начинает засорять пул потоков моего приложения?
  • достаточно ли Кливера компилятора для работы с 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();
        }
    }
}

5 ответов


используя Task.FromResult(0) или Task.FromResult<object>(null) будет нести меньше накладных расходов, чем создание Task с выражением no-op. При создании Task результат предопределен, нет планирования расходов.


сегодня, я бы рекомендовал использовать задач.CompletedTask для этого.


добавить Рид 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;

Task.Delay(0) как и в принятом ответе был хороший подход, так как это кэшированная копия завершенного Task.

по состоянию на 4.6 теперь Task.CompletedTask который более явен в своей цели, но не только делает Task.Delay(0) по-прежнему возвращает один кэшированный экземпляр, он возвращает то же самое один кэшированный экземпляр, как это делает Task.CompletedTask.

кэшированный характер ни того, ни другого не гарантируется постоянным, но как зависящие от реализации оптимизации, которые только реализация-зависит от оптимизации (то есть они все равно будут работать правильно, если реализация изменится на что-то, что все еще остается действительным) использование Task.Delay(0) было лучше, чем принятый ответ.


недавно столкнулся с этим и продолжал получать предупреждения/ошибки о том, что метод может быть аннулирован.

мы занимаемся умиротворением компилятора, и это проясняет его:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

это объединяет лучший из всех советов здесь до сих пор. Оператор return не требуется, если вы на самом деле не делаете что-то в методе.


Я предпочитаю Task completedTask = Task.CompletedTask; решение .Net 4.6, но другой подход-отметить метод async и вернуть void:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

вы получите предупреждение (функция CS1998 - Async без выражения await), но это безопасно игнорировать в этом контексте.