到 Task.Run 或不到 Task.Run
To Task.Run or not to Task.Run
假设我有一个包含异步方法的接口,并且我有该接口的两个不同实现。两种实现之一自然是异步的,而另一种则不是。 "most correct" 实现非异步方法的方法是什么?
public interface ISomething {
Task<Foo> DoSomethingAsync();
}
// Normal async implementation
public class Implementation1 : ISomething {
async Task<Foo> ISomething.DoSomethingAsync() {
return await DoSomethingElseAsync();
}
}
// Non-async implementation
public class Implementation2 : ISomething {
// Should it be:
async Task<Foo> ISomething.DoSomethingAsync() {
return await Task.Run(() => DoSomethingElse());
}
// Or:
async Task<Foo> ISomething.DoSomethingAsync() {
return DoSomethingElse();
}
}
我努力跟上 Stephen Cleary 的 blog,我知道这些都没有真正提供任何异步好处,我对此表示同意。第二个对我来说似乎更正确,因为它不会伪装成它不是的东西,但它确实给出了编译器警告,这些加起来会让人分心。
这一切都在 ASP.NET 中(Web MVC 和 WebAPI),如果有区别的话。
您可以完全放弃 async
修饰符并使用 Task.FromResult
同步完成 return 一个已完成的任务:
Task<Foo> ISomething.DoSomethingAsync()
{
return Task.FromResult(DoSomethingElse());
}
这会处理警告并具有更好的性能,因为它不需要 async
方法的状态机开销。
但是,这确实 change the semantics of exception handling 了一点。如果这是一个问题,那么您应该使用同步 async
方法方法并接受警告(或通过评论将其关闭):
#pragma warning disable 1998
async Task<Foo> ISomething.DoSomethingAsync()
#pragma warning restore 1998
{
return DoSomethingElse();
}
正如 Stephen Cleary 建议的那样,您还可以通过等待已完成的任务来处理该警告(同时保持方法同步):
async Task<Foo> 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<Foo> ISomething.DoSomethingAsync() {
Foo result;
// do something not hurting performance
// no I/O here
return Task.FromResult(result);
}
但是请注意,任何异常都会在调用方法时抛出,而不是在等待任务时抛出。对于与其他类型的工作兼容的后一种情况,您仍然应该使用异步:
async Task<Foo> ISomething.DoSomethingAsync() {
Foo result;
// do something not hurting performance
// no I/O here
return result;
}
cpu 密集工作
您应该使用 Task.Run
开始任务,并在任务中完成 cpu 密集工作。
Task<Foo> ISomething.DoSomethingAsync() {
return Task.Run(() => {
Foo result;
// do some CPU intensive work here
return result;
});
}
I/O 密集作业
您应该使用 async
关键字并等待任何异步 I/O 方法。不要使用同步 I/O 方法。
async Task<Foo> ISomething.DoSomethingAsync() {
Stream s = new ....
// or any other async I/O operation
var data = await s.ReadToEndAsync();
return new Foo(data);
}
假设我有一个包含异步方法的接口,并且我有该接口的两个不同实现。两种实现之一自然是异步的,而另一种则不是。 "most correct" 实现非异步方法的方法是什么?
public interface ISomething {
Task<Foo> DoSomethingAsync();
}
// Normal async implementation
public class Implementation1 : ISomething {
async Task<Foo> ISomething.DoSomethingAsync() {
return await DoSomethingElseAsync();
}
}
// Non-async implementation
public class Implementation2 : ISomething {
// Should it be:
async Task<Foo> ISomething.DoSomethingAsync() {
return await Task.Run(() => DoSomethingElse());
}
// Or:
async Task<Foo> ISomething.DoSomethingAsync() {
return DoSomethingElse();
}
}
我努力跟上 Stephen Cleary 的 blog,我知道这些都没有真正提供任何异步好处,我对此表示同意。第二个对我来说似乎更正确,因为它不会伪装成它不是的东西,但它确实给出了编译器警告,这些加起来会让人分心。
这一切都在 ASP.NET 中(Web MVC 和 WebAPI),如果有区别的话。
您可以完全放弃 async
修饰符并使用 Task.FromResult
同步完成 return 一个已完成的任务:
Task<Foo> ISomething.DoSomethingAsync()
{
return Task.FromResult(DoSomethingElse());
}
这会处理警告并具有更好的性能,因为它不需要 async
方法的状态机开销。
但是,这确实 change the semantics of exception handling 了一点。如果这是一个问题,那么您应该使用同步 async
方法方法并接受警告(或通过评论将其关闭):
#pragma warning disable 1998
async Task<Foo> ISomething.DoSomethingAsync()
#pragma warning restore 1998
{
return DoSomethingElse();
}
正如 Stephen Cleary 建议的那样,您还可以通过等待已完成的任务来处理该警告(同时保持方法同步):
async Task<Foo> 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<Foo> ISomething.DoSomethingAsync() {
Foo result;
// do something not hurting performance
// no I/O here
return Task.FromResult(result);
}
但是请注意,任何异常都会在调用方法时抛出,而不是在等待任务时抛出。对于与其他类型的工作兼容的后一种情况,您仍然应该使用异步:
async Task<Foo> ISomething.DoSomethingAsync() {
Foo result;
// do something not hurting performance
// no I/O here
return result;
}
cpu 密集工作
您应该使用 Task.Run
开始任务,并在任务中完成 cpu 密集工作。
Task<Foo> ISomething.DoSomethingAsync() {
return Task.Run(() => {
Foo result;
// do some CPU intensive work here
return result;
});
}
I/O 密集作业
您应该使用 async
关键字并等待任何异步 I/O 方法。不要使用同步 I/O 方法。
async Task<Foo> ISomething.DoSomethingAsync() {
Stream s = new ....
// or any other async I/O operation
var data = await s.ReadToEndAsync();
return new Foo(data);
}