519

在下面的代码中,由于接口的原因,该类LazyBar必须从其方法中返回一个任务(并且为了参数,不能更改)。如果LazyBars 的实现不寻常,因为它碰巧快速同步地运行 - 从该方法返回 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();
        }
    }
}
4

9 回答 9

742

今天,我建议使用Task.CompletedTask来完成此任务。


.net 4.6 之前:

使用Task.FromResult(0)or将比使用无操作表达式Task.FromResult<object>(null)创建更少的开销。Task在创建Task具有预定结果的结果时,不涉及调度开销。

于 2012-10-29T18:05:10.403 回答
201

要添加到Reed Copsey关于 using的答案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;
于 2014-11-22T02:07:09.643 回答
44

Task.Delay(0)因为在接受的答案中是一个好方法,因为它是已完成的缓存副本Task

从 4.6 开始,现在Task.CompletedTask它的目的更加明确,但不仅Task.Delay(0)仍然返回单个缓存实例,它返回与相同的单个缓存实例Task.CompletedTask

两者的缓存性质都不能保证保持不变,但是作为依赖于实现的优化,它们只是依赖于实现的优化(也就是说,如果实现更改为仍然有效的东西,它们仍然可以正常工作)使用Task.Delay(0)是比公认的答案更好。

于 2015-12-22T01:23:19.910 回答
26

最近遇到了这个问题,并且不断收到有关该方法无效的警告/错误。

我们的工作是安抚编译器,这可以清除它:

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

这汇集了迄今为止所有建议中最好的。除非您实际上在方法中做某事,否则不需要 return 语句。

于 2016-12-13T13:28:54.060 回答
22
return Task.CompletedTask; // this will make the compiler happy
于 2019-06-24T04:15:23.900 回答
13

当您必须返回指定类型时:

Task.FromResult<MyClass>(null);
于 2019-05-30T20:01:16.943 回答
2

我更喜欢Task completedTask = Task.CompletedTask;.Net 4.6 的解决方案,但另一种方法是将方法标记为 async 并返回 void:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

您会收到警告(CS1998 - 没有等待表达式的异步函数),但在这种情况下可以安全地忽略它。

于 2016-12-02T14:16:01.270 回答
-1
return await Task.FromResult(new MyClass());
于 2020-06-02T14:32:49.900 回答
-1

如果您使用的是泛型,所有答案都会给我们编译错误。您可以使用return default(T);. 下面的示例进一步解释。

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }
于 2020-02-14T06:37:23.413 回答