这些等待方法有什么区别?

Gra*_*lov 6 c# asynchronous task async-await

我正在研究C#中的一些异步编程,并想知道这些函数之间的区别在于它们完全相同并且都是等待的.

public Task<Bar> GetBar(string fooId)
{
    return Task.Run(() =>
    {
        var fooService = new FooService();
        var bar = fooService.GetBar(fooId);
        return bar;
    });
}

public Task<Bar> GetBar(string fooId)
{
    var fooService = new FooService();
    var bar = fooService.GetBar(fooId);
    return Task.FromResult(bar)
}

public async Task<Bar> GetBar(string fooId)
{
    return await Task.Run(() =>
    {
        var fooService = new FooService();
        var bar = fooService.GetBar(fooId);
        return bar;
    });
}
Run Code Online (Sandbox Code Playgroud)

我的猜测是,第一种是正确的做事方式,并且在您尝试从返回的Task获取结果之前,代码不会执行.

在第二种情况下,代码在调用时执行,结果存储在返回的任务中.

第三种有点像第二种?代码是在调用时执行的,Task.Run的结果是返回?在这种情况下,这个函数会有点愚蠢吗?

我是对的还是离开的?

Kir*_*kiy 6

这些方法实现都没有意义.您所做的只是将阻塞工作推送到线程池(或者更糟糕的是,同步运行它并将结果包装到Task<Bar>实例中).你应该做的是暴露同步API,让调用者决定如何调用它.无论他们是否想要使用Task.Run,都取决于他们.

话虽如此,这里有不同之处:

#1

第一个变体(直接返回Task<Bar>创建的via Task.Run)是"最纯粹的",即使它从API的角度来看没有多大意义.您允许Task.Run在线程池上安排给定的工作,并将Task<Bar>表示异步操作完成的表示返回给调用者.

#2

第二种方法(利用Task.FromResult)不是异步的.它同步执行,就像常规方法调用一样.结果只包含在一个完整的Task<Bar>实例中.

#3

这是第一个更复杂的版本.你正在实现类似于#1的结果,但是有额外的,不必要的,甚至有点危险await.这个值得更详细地看一下.

async/await通过将表示异步工作的多个s 组合成一个单元()来链接异步操作非常有用.它可以帮助您按正确的顺序进行操作,为您提供异步操作之间丰富的控制流,并确保在正确的线程上发生事情.TaskTask

但是,上述情况都不会对您的方案有任何好处,因为您只有一个Task.因此,没有必要让编译器为您生成状态机,只是为了完成Task.Run已经完成的工作.

设计不良的async方法也可能是危险的.通过不使用ConfigureAwait(false)你的awaited,Task你无意中引入了SynchronizationContext捕获,杀死性能并引入死锁风险,没有任何好处.

如果您的调用者决定Task<Bar>在具有SynchronizationContext(即Win Forms,WPF和可能的 ASP.NET)的环境中通过GetBar(fooId).Wait()或阻止您,GetBar(fooId).Result则由于此处讨论的原因,它们将陷入僵局.