C#中“返回等待”的目的是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19098143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:05:03  来源:igfitidea点击:

What is the purpose of "return await" in C#?

c#.net.net-4.5async-await

提问by TX_

Is there anyscenario where writing method like this:

没有这样的写法的场景:

public async Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return await DoAnotherThingAsync();
}

instead of this:

而不是这个:

public Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return DoAnotherThingAsync();
}

would make sense?

会有意义吗?

Why use return awaitconstruct when you can directly return Task<T>from the inner DoAnotherThingAsync()invocation?

return await当您可以直接Task<T>从内部DoAnotherThingAsync()调用返回时,为什么要使用构造?

I see code with return awaitin so many places, I think I might have missed something. But as far as I understand, not using async/await keywords in this case and directly returning the Task would be functionally equivalent. Why add additional overhead of additional awaitlayer?

return await在很多地方看到代码,我想我可能错过了一些东西。但据我所知,在这种情况下不使用 async/await 关键字并直接返回任务在功能上是等效的。为什么要增加额外await层的额外开销?

回答by Stephen Cleary

If you don't need async(i.e., you can return the Taskdirectly), then don't use async.

如果不需要async(即可以Task直接返回),则不要使用async.

There are some situations where return awaitis useful, like if you have twoasynchronous operations to do:

在某些情况下return await很有用,例如如果您有两个异步操作要做:

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

For more on asyncperformance, see Stephen Toub's MSDN articleand videoon the topic.

有关async性能的更多信息,请参阅 Stephen Toub 的MSDN 文章和有关该主题的视频

Update:I've written a blog postthat goes into much more detail.

更新:我写了一篇博客文章,其中详细介绍了更多细节。

回答by Servy

The only reason you'd want to do it is if there is some other awaitin the earlier code, or if you're in some way manipulating the result before returning it. Another way in which that might be happening is through a try/catchthat changes how exceptions are handled. If you aren't doing any of that then you're right, there's no reason to add the overhead of making the method async.

您想要这样做的唯一原因是await早期代码中是否还有其他代码,或者您是否在返回结果之前以某种方式对其进行了操作。另一种可能发生的方式是通过try/catch改变处理异常的方式。如果您什么都不做,那么您是对的,没有理由增加制作方法的开销async

回答by svick

There is one sneaky case when returnin normal method and return awaitin asyncmethod behave differently: when combined with using(or, more generally, any return awaitin a tryblock).

有一个偷偷摸摸情况下,当return在正常的方法和return awaitasync方法行为不同:当与组合using(或,更一般地,任何return awaittry块)。

Consider these two versions of a method:

考虑这两个版本的方法:

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

The first method will Dispose()the Fooobject as soon as the DoAnotherThingAsync()method returns, which is likely long before it actually completes. This means the first version is probably buggy (because Foois disposed too soon), while the second version will work fine.

第一个方法会Dispose()在方法返回后Foo立即处理对象DoAnotherThingAsync(),这可能在它实际完成之前很久。这意味着第一个版本可能有问题(因为Foo处理得太快),而第二个版本可以正常工作。

回答by Andrew Arnott

Another case you may need to await the result is this one:

您可能需要等待结果的另一种情况是:

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

In this case, GetIFooAsync()must await the result of GetFooAsyncbecause the type of Tis different between the two methods and Task<Foo>is not directly assignable to Task<IFoo>. But if you await the result, it just becomes Foowhich isdirectly assignable to IFoo. Then the async method just repackages the result inside Task<IFoo>and away you go.

在这种情况下,GetIFooAsync()必须等待结果,GetFooAsync因为T两种方法之间的类型不同,Task<Foo>不能直接赋值给Task<IFoo>。但是如果你等待结果,它就会变成Foowhich可以直接赋值给IFoo。然后 async 方法只是将结果重新打包,Task<IFoo>然后离开。

回答by Andrew Arnott

Making the otherwise simple "thunk" method async creates an async state machine in memory whereas the non-async one doesn't. While that can often point folks at using the non-async version because it's more efficient (which is true) it also means that in the event of a hang, you have no evidence that that method is involved in the "return/continuation stack" which sometimes makes it more difficult to understand the hang.

使原本简单的“thunk”方法异步会在内存中创建一个异步状态机,而非异步的则不会。虽然这通常可以指向人们使用非异步版本,因为它更有效(这是真的),但这也意味着在挂起的情况下,您没有证据表明该方法涉及“返回/继续堆栈”这有时会使理解挂起变得更加困难。

So yes, when perf isn't critical (and it usually isn't) I'll throw async on all these thunk methods so that I have the async state machine to help me diagnose hangs later, and also to help ensure that if those thunk methods ever evolve over time, they'll be sure to return faulted tasks instead of throw.

所以是的,当 perf 不重要(通常不是)时,我会在所有这些 thunk 方法上抛出异步,以便我有异步状态机来帮助我稍后诊断挂起,并帮助确保如果那些thunk 方法随着时间的推移不断发展,它们肯定会返回错误的任务而不是抛出。

回答by heltonbiker

This also confuses me and I feel that the previous answers overlooked your actual question:

这也让我感到困惑,我觉得之前的答案忽略了您的实际问题:

Why use return await construct when you can directly return Task from the inner DoAnotherThingAsync() invocation?

当您可以直接从内部 DoAnotherThingAsync() 调用返回 Task 时,为什么要使用 return await 构造?

Well sometimes you actuallywant a Task<SomeType>, but most time you actually want an instance of SomeType, that is, the result from the task.

好吧,有时您实际上想要的是Task<SomeType>,但大多数时候您实际上想要的是 的实例SomeType,即任务的结果。

From your code:

从您的代码:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

A person unfamiliar with the syntax (me, for example) might think that this method should return a Task<SomeResult>, but since it is marked with async, it means that its actual return type is SomeResult. If you just use return foo.DoAnotherThingAsync(), you'd be returning a Task, which wouldn't compile. The correct way is to return the result of the task, so the return await.

不熟悉语法的人(例如我)可能认为该方法应该返回 a Task<SomeResult>,但由于它被标记为async,这意味着它的实际返回类型是SomeResult。如果您只是使用return foo.DoAnotherThingAsync(),您将返回一个无法编译的任务。正确的方法是返回任务的结果,所以return await.

回答by haimb

If you won't use return await you could ruin your stack trace while debugging or when it's printed in the logs on exceptions.

如果您不使用 return await ,则可能会在调试时或在异常日志中打印堆栈跟踪时破坏堆栈跟踪。

When you return the task, the method fulfilled its purpose and it's out of the call stack. When you use return awaityou're leaving it in the call stack.

当您返回任务时,该方法实现了它的目的并且它在调用堆栈之外。当您使用时,return await您将它留在调用堆栈中。

For example:

例如:

Call stack when using await: A awaiting the task from B => B awaiting the task from C

使用 await 时的调用堆栈:A 正在等待来自 B 的任务 => B 正在等待来自 C 的任务

Call stack when notusing await: A awaiting the task from C, which B has returned.

使用 await时的调用堆栈:A 正在等待来自 C 的任务,而 B 已返回该任务。