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

Jon*_*Rea 390 .net c# threadpool task-parallel-library async-await

在下面的代码中,由于接口,类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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ree*_*sey 559

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


今天,我建议使用Task.CompletedTask来实现这一目标.

  • 我不确定,但'Task.CompletedTask`可能会做到这一点!(但需要.net 4.6) (17认同)
  • @Legends不能直接创建任务 (7认同)
  • 如果您碰巧使用https://github.com/StephenCleary/AsyncEx,它们提供了一个TaskConstants类来提供这些已完成的任务以及其他几个非常有用的任务(0 int,true/false,Default <T>()) (5认同)
  • 返回默认值(YourReturnType); (2认同)

i3a*_*non 178

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

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}
Run Code Online (Sandbox Code Playgroud)

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


.Net框架(V4.6)的最新版本增加了这一点与Task.CompletedTask静态属性

Task completedTask = Task.CompletedTask;
Run Code Online (Sandbox Code Playgroud)

  • @Pixar我不清楚.我的意思是"'不同步'会更有效率".使方法async指示编译器将其转换为状态机.每次调用它时,它也会创建一个新任务.返回已完成的任务将更清晰,更高效. (3认同)
  • @Asad它减少了分配(以及它的GC时间).每次需要完成的任务时,您只需执行一次,而不是分配新内存并构建Task实例. (3认同)

Jon*_*nna 36

Task.Delay(0)因为它是一个很好的方法,因为它是一个完整的缓存副本Task.

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

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

  • 我不知道为什么,但是`Task.CompletedTask`不能在PCL项目中使用,即使我将.net版本设置为4.6(配置文件7),只是在VS2017中测试过. (2认同)

Ale*_*zzi 16

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

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

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }
Run Code Online (Sandbox Code Playgroud)

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

  • 那是完全错误的.您收到编译器错误,因为方法定义包含async,因此编译器需要await."正确"用法将是公共任务MyVoidAsyncMethog(){return Task.CompletedTask;} (14认同)
  • 他并非完全错误,他只是删除了异步关键字.我的方法更具惯用性.他很简约.如果不是有点粗鲁. (4认同)
  • 不知道为什么这被否决了,因为这似乎是最干净的答案 (2认同)
  • 因为基思的评论. (2认同)
  • 这毫无意义,完全同意 Keith 的观点,实际上我没有得到所有的赞成。为什么要添加不必要的代码?`public Task MyVoidAsyncMethod() {}` 与上述方法完全相同。如果有像这样使用它的用例,请添加附加代码。 (2认同)

小智 7

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

Task.FromResult<MyClass>(null);
Run Code Online (Sandbox Code Playgroud)


Xin*_*Xin 7

return Task.CompletedTask; // this will make the compiler happy
Run Code Online (Sandbox Code Playgroud)