首页 文章

如果我的接口必须返回Task,那么实现无操作的最佳方法是什么?

提问于
浏览
315

在下面的代码中,由于接口,类 LazyBar 必须从它返回一个任务's method (and for arguments sake can't被更改) . 如果 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();
        }
    }
}

5 回答

  • 158

    与使用no-op表达式创建 Task 相比,使用 Task.FromResult(0)Task.FromResult<object>(null) 将产生更少的开销 . 在创建具有预先确定结果的 Task 时,不会涉及调度开销 .


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

  • 12

    要添加Reed Copsey's answer关于使用 Task.FromResult ,如果缓存已完成的任务,则可以进一步提高性能,因为已完成任务的所有实例都相同:

    public static class TaskExtensions
    {
        public static readonly Task CompletedTask = Task.FromResult(false);
    }
    

    使用 TaskExtensions.CompletedTask ,您可以在整个应用程序域中使用相同的实例 .


    latest version of the .Net Framework (v4.6)只添加Task.CompletedTask静态属性

    Task completedTask = Task.CompletedTask;
    
  • 29

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

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

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

  • 456

    最近遇到这个并且不断收到关于该方法无效的警告/错误 .

    我们正在安抚编译器并清除它:

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

    到目前为止,这里汇集了所有建议中的最佳建议 . 除非您实际在该方法中执行某些操作,否则不需要返回语句 .

  • 3

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

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
        }
    

    您将收到警告(CS1998 - 没有等待表达式的异步函数),但在此上下文中可以安全地忽略 .

相关问题