我相信我们都会同意同步优于异步并不是一个好主意。TAP 模型极大地简化了使用 async/await 的异步调用,这就是正确的方法。
不幸的是,对于那些坚持使用旧技术的人来说,这不是一个选择。例如,.NET Framework 中的 Web 服务 (asmx) 不支持 TAP。在大型遗留解决方案中,维护重复的调用链(同步和异步)可能非常令人生畏和烦人。此外,即使您不知道,您也可能正在使用“同步而非异步”方法。许多提供同步 API 的库本质上在内部只是其异步 API 的包装器。例如 Rebus、RestSharp 等。
这是这篇文章/问题的主要目的。尽管我对 async/await 有相当的了解,但我不会假装我知道所有的极端情况(而且有很多)。所以,我想知道专家们对这个话题的看法。这种实施方式可能存在哪些缺点?
https://github.com/restsharp/RestSharp/blob/dev/src/RestSharp/AsyncHelpers.cs
与许多“同步优于异步”实现不同,它们通常只是启动一个新线程以避免可能的死锁Task.Run(async () => {await ...}).Result;此实现使用自定义同步上下文来避免死锁,同时保持在同一线程上。这提供了甚至访问的能力HttpContext.Current(对于 NETFX 应用程序)。
我已经在各种场景中尝试过这个助手,并且效果相当好。但是,我想进一步了解可能出错的地方。
那么,这有多错误呢?有哪些陷阱?
不幸的是,对于那些坚持使用旧技术的人来说,这不是一个选择。
我确实有一篇文章讨论了异步同步的几种可能方法,并对每种方法的缺点进行了简短讨论。
例如,.NET Framework 中的 Web 服务 (asmx) 不支持 TAP。
不,但 ASMX 确实支持 APM,您可以编写一个简短的互操作层来将核心 TAP 逻辑公开给 ASMX 并始终保持异步。有一些关于如何做到这一点的示例以及一些TAP-to-APM 帮助程序,它们使互操作层非常干净。
需要同步异步的场景很少;当转换代码不是业务优先事项时,保留一些真正应该异步的代码同步通常是一个务实的决定。
在大型遗留解决方案中,维护重复的调用链(同步和异步)可能非常令人生畏和烦人。
100%同意。
我首选的解决方案是Brownfield Async 文章中“布尔参数 hack”的更现代版本,该解决方案最初是由 Stephen Toub 在对该文章进行技术审查时向我展示的。这是一种使代码保持异步或同步的技术,但不需要任何重复的逻辑。
最近,Stephen Toub 在他关于 .NET 7 性能改进的文章中展示了更现代版本的“布尔参数黑客” (这是一篇很大的文章;搜索“与读写性能相关的一个最终更改”以找到相关的部分)。我拿出了那个宝石并写了一篇关于它的更具体的博客文章。该代码乍一看很奇怪,但它是一种非常强大的技术,这就是我向所有现代库推荐的技术。
这种实施方式可能存在哪些缺点?
此实现安装一个SynchronizationContext包含工作队列的自定义,对初始工作项进行排队,然后同步等待其工作队列完成。它与AsyncContext我的 AsyncEx 库类似。我相信该实现最初源自这个旧的论坛帖子,我在几个地方看到过它的复制,通常带有诸如“我不知道这是如何工作的”之类的评论,说实话这对我来说有点可怕。我从 SO 和其他地方获取代码,但只有在完全理解它之后。
您可以说它是Brownfield Async 文章中的“嵌套消息循环黑客”的变体。AsyncHelpers.RunSync控制当前线程并将其转变为消息泵,处理其自己的工作队列。它安装自己的SynchronizationContext来捕获async延续(默认情况下在捕获上恢复SynchronizationContext或TaskScheduler如我在我的博客中描述的那样)。
因此,您将遇到的极端情况都与交换有关SynchronizatonContext。可能无法提供详尽的清单,但我脑海中浮现出一些担忧:
SynchronizationContext. 一个例子是在 Core ASP.NET出现之前,如果SynchronizationContext.Current不是AspNetSynchronizationContext. 我真的不知道他们为什么这样做,但这是我多年前尝试这种黑客攻击时观察到的行为。另一个例子,一些 UI 组件将验证它们位于正确的同步上下文中(其他组件则验证它们位于正确的线程上)上,如果 SyncCtx 被交换,该线程仍然可以正常工作)。SynchronizationContext供以后使用,但SynchronizationContext这里使用的寿命有限;一旦任务完成,整个 SyncCtx 就会被拆除。因此,Progress<T>在 SyncCtx 被拆除后,不得使用任何类似于 SyncCtx 的 Rx 可观察观察的东西。RunSync窗口主消息处理循环的一部分运行)。现在,这些帖子已被删除,现代 .NET CoreAutoResetEvent.WaitOne最终位于WaitForMultipleObjectsIgnoringSyncContext,从名称上看,它可能没有部分发挥作用,所以也许这不再是事实了。但对于我自己来说,我会非常谨慎地在 STA/GUI 线程上做这样的事情。总而言之,这不是我推荐的方法。我建议改用通用值类型约束接口方法。但是,如果您对将同步运行的所有代码有深入的了解,并且确定不会遇到上述情况,那么这是可以接受的。
| 归档时间: |
|
| 查看次数: |
737 次 |
| 最近记录: |