【问题标题】:Consequences of making an Async function synchronous使 Async 函数同步的后果
【发布时间】:2016-07-22 20:59:56
【问题描述】:

(如果问题的某些部分/部分被认为“太基本”,我提前道歉。我已经完成了关于 async-await 的研究,但对我来说这不是一个容易掌握的概念。要么或者我没有找到合适的资源。)

我一直读到(或者我是这么解释的)“使异步调用同步的方法是将 'await' 放在它前面”。

所以这个语句产生的代码如下简单代码所示:

// CODE 1:
public void async MyFunction()
{
    await SomeFunctionAsync();
}

即:1) 将await 放在Async 函数的前面,2) 将async 放在你的函数中,因为现在它包含一个await

但根据我的经验,有时会表现得有点“有趣”。我注意到的症状是,有时它的行为就像一个不受我控制的线程。

所以第一个问题是:当你调用MyFunction()并且这个函数包含async关键字时会发生什么?我的理解是(令人困惑)它不应该被称为MyFunctionAsync(),因为它是为返回Task的函数保留的。

...所以我想出的替代方案(似乎始终像同步类一样)是这种类型的代码:

// CODE 2:
public void MyFunction()
{
    var task = SomeFunctionAsync();
    Task.WaitAll(task);
}

第二个问题:有人可以解释CODE 2 的缺点(如果有的话)吗?

另外,隐含的问题是我有兴趣了解我对异步等待模式可能有的任何误解。

【问题讨论】:

  • “当你调用 MyFunction() 并且这个函数包含 async 关键字时会发生什么?”它将异步执行,但您不能等待它,因为它不返回任务。这就是为什么它似乎摆脱了你的控制。回答您的第二个问题,请参阅stackoverflow.com/questions/9343594/…

标签: c# async-await


【解决方案1】:

我一直读到(或者我是这么解释的)“使异步调用同步的方法是将 'await' 放在它前面”。

一点也不。这是调用异步代码的串行方式,而不是同步的说法。 “同步”的意思是“阻塞当前线程”,await 不会阻塞当前线程。然而,“串行”的意思是“在下一件事情之前做这件事”,await 会做那件事。更多信息,请查看我的async intro

使用await 是调用异步方法最自然、最常用的方式。请注意,通常当您向方法添加 async 时,您还应该将返回类型void 更改为 Task。编译器将指导您:如果您只输入 await 而没有 async,它将非常明确地为您建议下一个更改。

但根据我的经验,有时会表现得有点“有趣”。我注意到的症状是有时它的行为就像一个不受我控制的线程。

这是因为您的代码使用的是async void 而不是async Taskasync void 不自然;您没有“控制权”,因为您没有返回Task。更自然的方法是让方法async Taskawait它的 调用者执行该任务,依此类推。 async 很自然地会像这样在你的堆栈中“增长”——我们称之为“async all the way”。

所以第一个问题是:当你调用 MyFunction() 并且这个函数包含 async 关键字时会发生什么?我的理解是(非常令人困惑)它不应该被称为 MyFunctionAsync() 因为它是为返回任务的函数保留的。

您对命名的理解是正确的。 MyFunction 不是自然的 (Task-returning) 异步方法。

async void 方法很奇怪,不像普通的async 方法。他们奇怪的行为是我推荐avoid async void 的原因之一。但既然你问...

async void 方法中的await 与常规async 方法中的await 一样工作(正如我在async 介绍中所描述的那样):它将捕获当前上下文(当前@987654352 @ 或 TaskScheduler,并在异步操作完成时在该上下文中继续执行 async void 方法。这就是为什么它就像“不受控制的线程” - 它只是在需要时恢复执行。你没有办法检测何时完成,因为该方法返回 void 而不是 Task

例外是事情变得更加奇怪的地方。 async void 方法在其执行开始时捕获当前的SynchronizationContext,如果异常从async void 方法中逃脱,它将在其捕获的SynchronizationContext 上重新引发该异常。这通常会导致进程级崩溃。

第二个问题:有人能解释一下 CODE 2 的缺点(如果有的话)吗?

它很容易引起我在博客中描述的deadlock situation。它还将任务中的任何异常包装在 AggregateException 中。

底线是:不要阻塞异步代码。如果你无论如何都要阻止它,那么为什么首先让它异步呢?任何混合的阻塞和异步代码都应该被视为具有相当高优先级的技术债务。没有适用于所有情况的解决方案,但是您可以临时使用各种技巧,同时将代码升级为适当的异步;我在brownfield async development 上的一篇文章中描述了它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-14
    • 2021-04-10
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    相关资源
    最近更新 更多