C# 返回任务的方法的不同实现

C# 返回任务的方法的不同实现,c#,asynchronous,task-parallel-library,C#,Asynchronous,Task Parallel Library,这是我在重构一些遗留代码时遇到的问题 考虑一个接口上返回任务的方法: public interface IFoo { Task Bar(); } public class Foo1 : IFoo { public Task Bar() { return Task.Run(() => { /* some work */ }); } } Bar方法实现可以通过两种

这是我在重构一些遗留代码时遇到的问题

考虑一个接口上返回
任务的方法

public interface IFoo
{
    Task Bar();
}
public class Foo1 : IFoo
{
    public Task Bar()
    {
        return Task.Run(() =>
            {
                /* some work */
            });
    }
}
Bar
方法实现可以通过两种方式实现:

返回
任务

public interface IFoo
{
    Task Bar();
}
public class Foo1 : IFoo
{
    public Task Bar()
    {
        return Task.Run(() =>
            {
                /* some work */
            });
    }
}
或者使用
async
<代码>等待:

public class Foo2 : IFoo
{
    public async Task Bar()
    {
        await Task.Run(() =>
            {
                /* some work */
            });
    }
}

这些实现在功能上是等效的,还是存在(可能细微的)差异?

这是一个很大的差异,因为使用异步等待语法会导致编译器生成代码,一旦任务完成,代码实际上会继续通过等待语句。此外,等待出现故障的任务会导致异常冒泡到等待者,这意味着它不再被视为未被观察到。。我认为还有更多,我真的建议你看一下Jon Skeet在pluralsight上的C#5异步课程,他实际上是通过编译器生成的代码来解释的

返回任务

Foo1.Bar
只是一个常规的同步方法,它返回一些对象实例(
Task

或者使用异步。。。等待

Foo2.Bar
是一种异步方法,它将被编译成状态机。这里会有一些开销。 请注意,类似这样的Roslyn方法的未来版本将转换为同步方法


但是,实际上,您不应该使用Task.Run。有关详细信息,请参阅这篇文章。

好吧,第二个实现看起来很复杂。如果您只需要返回一个任务,那么创建一个任务并像第一个实现一样返回它


第二个实现返回一个任务,该任务封装了另一个等待的任务。这将产生不必要的代码膨胀、内存使用和可能的另一个线程创建,只是为了实现与第一个线程相同的目标

据我所知,差别并不像你在这里所说的那样大。当您实现Foo1时,您需要调用Task.Wait(),而对于Foo2,只需等待Task,所以有区别。此处解释:@RazvanDumitru为什么要调用
Task.Wait()
?就调用方而言,这两种方法是相同的<代码>等待foo1Instance.Bar()
等待foo2Instance.Bar()将以同样的方式工作。没有理由为了返回已完成的任务而阻止第一个任务。你是说别的吗?是的。你说得对。根本不需要等待。您可以等待两个返回的对象。第二个代码段允许在任务完成后执行更多代码,前提是不引发异常。。不管怎么说,目标并没有被讨论——问题是围绕着两者之间的区别。@EyalPerry,这是错误的。您可以使用
ContinueWith
在任务完成后运行代码。这就是任务在.NET 4.0中的使用方式,当由
async/await
生成的状态机不受欢迎时,任务仍在使用。您所说的是正确的,但并不否定我所说的@这篇文章不是这么说的。最后它确实使用了Task.Run。建议不要在方法的实现中使用Task.Run自动将CPU绑定的方法转换为“异步”方法。另外,这两种方法都是异步的。两者都使用单独的任务执行工作。但第二个实际上在等待其他异步调用。@PanagiotisKanavos:不同意。第一个是同步的。它只是启动一些任务,但方法本身并不异步——从方法启动任务或线程不会使其异步。还有,你误解了链接的帖子。当实现不是“真”异步代码时,允许调用方决定是否使用
Task.Run