To Task.Run or not to Task.Run

本文关键字:Task Run to not or To | 更新日期: 2024-10-30 17:42:30

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

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的博客,我知道这些博客实际上都没有提供任何异步的好处,我对此没意见。第二个对我来说似乎更正确,因为它不会假装不是,但它确实给出了编译器警告,这些警告加起来会分散注意力。

这一切都将在 ASP.NET(Web MVC和WebAPI)中,如果这有所作为的话。

To Task.Run or not to Task.Run

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

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

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

但是,这确实会稍微改变异常处理的语义。如果这是一个问题,那么您应该使用同步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 工作量可忽略不计
  • 中央处理器密集型工作
  • 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;
}

中央处理器密集型工作

您应该使用 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);
}