【问题标题】:What does async guarantee?异步保证什么?
【发布时间】:2015-10-01 01:23:47
【问题描述】:

如果一个方法被标记为异步,那有什么保证吗?

如果不是,调用异步方法以确保非阻塞行为的最佳做法是什么? (尤其是调用第三方库时)


加长版:

到目前为止,我假设等待异步方法保证不会阻塞线程(如果实现正确)。但是,我最近有this situation(也发生在others)。 在这种情况下,(非)阻塞行为取决于 EF 提供程序。由于 UI 层不应该真正了解或关心 DB 提供程序的实现细节,我认为将所有调用包装在 Task.Run 中以确保非阻塞行为是有意义的。但是,除了 UI 层之外,我在任何地方都不需要异步,这让我质疑始终保持异步的理念。

【问题讨论】:

  • 你必须await调用异步方法iirc
  • 这需要一些工作才能完全布局。但有一件事它保证,那就是你不会受到你在异步方法内外调用的任何代码的副作用。
  • Hereasync-await上的一组文章。

标签: c# async-await


【解决方案1】:

标记一个方法async 并没有真正提供任何保证,当然也不能保证该方法将异步运行。

它做了什么(但我不会称之为保证):

  • 允许在方法中使用await 关键字(但强制使用它!)。
  • 它确实强制方法的返回类型为voidTaskTask<TResult>。在最后两种情况下,它使编译器能够为您生成知道如何在 Task 中设置返回值的代码,或者为您透明地传播未处理的异常。

但为了强调:它确实保证该方法将以异步方式编码。你可以自己检查一下。编写一个普通方法,将其标记为async,然后“忘记”到await 方法内的任何内容。您应该从编译器那里收到有关该方法正在同步的警告,但这是允许的。

我认为您会发现通过Async/Await FAQ 来解决您的一些疑问很有用:

在方法上使用 async 关键字是否会强制对该方法的所有调用都是异步的?

没有。当您调用标记为async 的方法时,它开始在当前线程上同步运行。因此,如果您有一个返回 void 的同步方法,并且您所做的只是将其标记为 async,则该方法的调用仍将同步运行。无论您将返回类型保留为void 还是将其更改为Task,这都是正确的。类似地,如果您有一个返回一些TResult 的同步方法,而您只需将其标记为async 并将返回类型更改为Task<TResult>,则该方法的调用仍将同步运行。

将方法标记为“异步”不会影响该方法是同步运行完成还是异步完成。

【讨论】:

  • 所以基本上我总是必须将调用包装在 Task.Run 中,如果我想绝对确定它将异步运行并且不会阻塞某个深处。如果我这样做了,那么除了调用部分之外,在任何地方都有异步没有任何好处。
  • "当然不保证方法会同步运行" 你的意思是异步吗?
  • @Luka:不。这只是意味着您需要在每个异步调用上做功课,以确保它的行为也符合您的预期。盲目地将所有内容包装在 Task.Run() 调用中会破坏 async/await 的目的,即提供异步性同时减少使用的线程数。
  • @sstan - 你的论点很有道理。但是,请从我的问题中的链接中考虑这种情况。 IMO,切换 EF 提供程序不应影响 UI 层的设计(从 dev Compact 切换到 prod Server 是真实世界的场景)。您建议如何处理这种情况?
【解决方案2】:

我认为将所有调用包装在 Task.Run 中以确保非阻塞行为是有意义的

我认为不会。虽然您确实无法保证,但大多数 async 方法实际上是异步的。所以我认为只有在你发现一个方法(或它的某些实现)实际上是阻塞之后才应该将调用包装在 Task.Run() 中,而不是在此之前。

【讨论】:

    【解决方案3】:

    如果一个方法被标记为异步,那有什么保证吗?

    不,正如其他人所说,它不能保证实际操作是异步的。这只是提示您了解可能是异步操作。您主要依靠实现这些异步方法的人来实际公开真正的异步操作,而不是使用 Task.Run 等“假异步”来欺骗您。

    比如这个方法是Task返回:

    public Task<string> DoHeavyLiftingAsync()
    {
        var tcs = new TaskCompletionSource<string>();
        string result = SomeHeavyWork();
        return tcs.SetResult(result);
    }
    

    基本上,没有执行任何异步操作。这会发生吗?是的,如果实施它的人没有意识到他向调用者保证了什么,这可能会发生。

    如果不是,调用异步方法的最佳实践是什么? 确保非阻塞行为? (尤其是打电话给第三方时 图书馆?

    通常,您可以相信 BCL 中的 must async 实现可以为您提供真正的异步(尽管 you may be surprised at times)。如果您使用第三方 API,您始终可以使用 .NET 反编译器查看代码,以了解幕后实际发生的情况。如果这样做之后,您仍然不确定,您可以随时使用Task.Run 结束通话。确保将其用作最后的手段。

    【讨论】:

      猜你喜欢
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多