到Task.Run或不到Task.Run

时间:2015-03-08 17:34:34

标签: c# .net asynchronous task-parallel-library async-await

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

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)中进行。

2 个答案:

答案 0 :(得分:8)

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

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();
}

答案 1 :(得分:2)

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

  • 没有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:

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);
}