如果我的接口必须返回任务什么是最好的方式有一个无操作实现?

在下面的代码中,由于接口的原因,类LazyBar必须从它的方法返回一个任务(为了参数的缘故,不能更改)。如果LazyBars的实现是不寻常的,因为它恰好快速和同步地运行-从方法中返回一个无操作任务的最佳方法是什么?

我已经使用了下面的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();
}
}
}
241885 次浏览

今天,我建议使用的任务。CompletedTask来完成这个任务。


Pre .net 4.6:

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

要在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;

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

在4.6版本中,现在有了Task.CompletedTask,它的目的更加明确,但不仅Task.Delay(0)仍然返回单个缓存实例,它还返回相同单个缓存实例,就像Task.CompletedTask一样。

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

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

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
}

您将得到一个警告(CS1998 - Async函数没有await表达式),但在此上下文中忽略该警告是安全的。

最近遇到了这个问题,并不断得到关于方法为空的警告/错误。

我们的工作是安抚编译器,这就澄清了问题:

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

这是到目前为止最好的建议。没有return语句是必要的,除非你真的在方法中做一些事情。

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

Task.FromResult<MyClass>(null);
return Task.CompletedTask; // this will make the compiler happy

如果你使用泛型,所有答案都会给我们编译错误。您可以使用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);
}


}
return await Task.FromResult(new MyClass());