到Task.Run或不运行Task.Run

假设我有一个包含异步方法的接口,我有两个不同的接口实现。 两种实现中的一种是自然异步,而另一种则不是。 实现非异步方法的“最正确”方法是什么?

public interface ISomething { Task DoSomethingAsync(); } // Normal async implementation public class Implementation1 : ISomething { async Task ISomething.DoSomethingAsync() { return await DoSomethingElseAsync(); } } // Non-async implementation public class Implementation2 : ISomething { // Should it be: async Task ISomething.DoSomethingAsync() { return await Task.Run(() => DoSomethingElse()); } // Or: async Task ISomething.DoSomethingAsync() { return DoSomethingElse(); } } 

我试着跟上Stephen Cleary的博客 ,我知道其中任何一个都没有提供任何异步的好处,而且我对此很满意。 第二个对我来说似乎更正确,因为它不会假装不是它的东西,但它确实给出了编译器警告,并且这些加起来并且分散注意力。

如果这会产生影响,那么这些都将在ASP.NET(Web MVC和WebAPI)中。

您可以完全放弃async修饰符并使用Task.FromResult同步返回已完成的任务:

 Task ISomething.DoSomethingAsync() { return Task.FromResult(DoSomethingElse()); } 

这会处理警告并具有更好的性能,因为它不需要async方法的状态机开销。

但是,这确实改变了exception处理的语义 。 如果这是一个问题,那么您应该使用同步async方法方法并接受警告(或通过注释将其关闭):

 #pragma warning disable 1998 async Task ISomething.DoSomethingAsync() #pragma warning restore 1998 { return DoSomethingElse(); } 

正如Stephen Cleary建议您也可以通过等待已完成的任务来处理该警告(同时保持方法同步):

 async Task ISomething.DoSomethingAsync() { await Task.FromResult(false); // or Task.CompletedTask in .Net 4.6 return DoSomethingElse(); } 

这实际上取决于你的方法在做什么:

  • 没有I / O,cpu工作量可以忽略不计
  • cpu密集的工作
  • I / O密集型工作

没有I / O,cpu工作量可以忽略不计

您应该同步计算结果并创建一个包含结果的Task。

 Task ISomething.DoSomethingAsync() { Foo result; // do something not hurting performance // no I/O here return Task.FromResult(result); } 

但请注意,调用方法时将抛出任何exception,而不是在等待任务时抛出exception。 对于符合其他类型工作的后一种情况,您应该使用async:

 async Task ISomething.DoSomethingAsync() { Foo result; // do something not hurting performance // no I/O here return result; } 

cpu密集的工作

您应该使用Task.Run启动任务并在任务中执行cpu密集型工作。

 Task ISomething.DoSomethingAsync() { return Task.Run(() => { Foo result; // do some CPU intensive work here return result; }); } 

I / O密集型工作

您应该使用async关键字并等待任何异步I / O方法。 不要使用同步I / O方法。

 async Task ISomething.DoSomethingAsync() { Stream s = new .... // or any other async I/O operation var data = await s.ReadToEndAsync(); return new Foo(data); }