我相信我们都会同意,同步而不是异步不是一个好主意.分路模型极大地简化了带有Async/AWait的异步调用,这就是解决之道.

不幸的是,对于任何坚持使用一些旧技术的人来说,这不是一个 Select .例如,.NET框架中的Web服务(Asmx)不支持TAP.在大型传统解决方案中,维护重复调用链(同步和异步)可能非常令人生畏和厌倦.此外,即使您没有意识到这一点,您也可能正在使用"同步重于异步"的方法.在本质上,许多提供同步API的库在内部只是对它们的异步API的包装.例如,Rebus、RestSharp等.

这就是这篇文章/问题的主要目的.即使我对Async/AWait有相当的了解,我也不会假装我知道所有的转角 case (有很多).所以,我想知道专家们对这个话题的看法.此实施可能存在哪些缺陷?

https://github.com/restsharp/RestSharp/blob/dev/src/RestSharp/AsyncHelpers.cs

不同于通常它们只是启动新线程以避免可能的死锁Task.Run(async () => {await ...}).Result的许多"基于异步的同步"实现;该实现使用定制的同步上下文来避免死锁,同时保持在相同的线程上.这提供了甚至访问HttpContext.Current的能力(对于NETFX应用程序).

我已经在不同的场景中try 过这个助手,它工作得相当好.但是,我想知道它在哪里可能出错的进一步意见.

那么,这有什么不对的呢?trap 是什么?

推荐答案

不幸的是,对于任何坚持使用一些旧技术的人来说,这不是一个 Select .

我有一个article人讨论了几种可能的异步同步方法,并简要讨论了每种方法的缺点.

例如,.NET框架中的Web服务(Asmx)不支持TAP.

不支持,但ASMX确实支持APM,您可以编写一个short interop layer来将您的核心分路逻辑expose 给ASMX,并始终保持异步状态.在Nito.AsyncEx中,有大约examples on SO个关于如何做到这一点的文档,还有大约TAP-to-APM helpers个文档,它们使得互操作层非常干净.

很少有require同步胜过异步的情况;当转换代码不是业务优先事项时,保留一些真正应该是异步的代码通常是一个务实的决定.

在大型传统解决方案中,维护重复调用链(同步和异步)可能非常令人生畏和厌倦.

100%同意.

我更喜欢的解决方案是Brownfield Async article中"布尔参数黑客"的更现代版本,这最初是Stephen Toub在那篇文章的技术 comments 中向我展示的.这是一种让代码保持一路异步或一路同步的技术,但不需要任何逻辑复制.

最近,Stephen Toub在他的article on .NET 7 performance improvements篇文章(这是一篇很长的文章;搜索与阅读和写作表现有关的最后一项改变)中展示了更现代版本的"布尔论点黑客"(Boolean Argument Hack),找到相关部分.我拿出那颗Ruby ,写了一个more specific blog post about it分.代码起初看起来很奇怪,但它是一种非常强大的技术,这是我推荐给所有现代库的.

此实施可能存在哪些缺陷?

此实现安装包含工作队列的自定义SynchronizationContext,对初始工作项进行排队,然后同步等待其工作队列完成.它与我的AsyncEx库中的AsyncContext相似.我相信实现最初是从this old forum post开始的,我在几个地方看到过它的复制,通常会有一条 comments ,比如"我不知道这是如何工作的",老实说,这对我来说有点可怕.我从SO和其他地方拿代码,但only后完全理解它.

您可以说它是从the Brownfield Async article开始的"嵌套消息循环黑客"的变体.AsyncHelpers.RunSync控制当前线程,并将其转换为消息泵,处理自己的工作队列.它安装了自己的SynchronizationContext来捕获async个延续(which by default resume on the captured SynchronizationContext or TaskScheduler正如我在我的博客上描述的那样).

所以,你将要遇到的角落 case 都与那SynchronizatonContext个掉期有关.可能不可能列出一个详尽的 list ,但这里有一些我头顶上的担忧:

  1. 某些组件需要特定的SynchronizationContext.一个例子是在核心ASP.NET之前的日子里,如果SynchronizationContext.Current不是AspNetSynchronizationContext,一些ASP.NETAPI将只有hang.我真的不知道他们为什么要这么做,但这是我在多年前try 这种黑客攻击时观察到的行为.作为另一个例子,一些UI组件将验证它们在正确的同步上下文中(其他组件验证它们在正确的thread上,如果SyncCtx被交换,它仍然工作得很好).
  2. 有些组件捕获SynchronizationContext以备后用,但这里使用的SynchronizationContext具有有限的生命周期;一旦任务完成,整个SyncCtx就会被拆除.因此,在SyncCtx被拆除后,不能使用任何像Progress<T>或在该SyncCtx上观察到的Rx观察.
  3. 此解决方案安装单线程上下文,然后在其上同步阻塞.因此,它解决了一类异步同步问题,但如果它调用的任何东西执行阻塞样式的异步同步,它肯定会死机.
  4. 最后一个问题是我最大的担忧之一,但也是最难解释的.在我的文章中,我把它写成"意想不到的再入".如果在UI线程(或更具体地说,STA线程)上运行,这种方法可能会产生令人惊讶的结果.CBrumme有一些很棒的博客文章,讲述了.NET运行时如何在阻塞时执行some STA泵送;遗憾的是,这些文章在几年前微软改变了他们的博客URI方案时被删除了.从本质上讲,这意味着某些Windows消息可能会被UI线程处理,即使从托管的Angular 来看,它被"阻止"了;这可能会导致您的代码的某些部分运行,而不是作为RunSync的一部分运行,而不是作为窗口的主消息处理循环运行.现在,这些帖子被删除了,这modern .NET Core AutoResetEvent.WaitOne ends up at WaitForMultipleObjectsIgnoringSyncContext个帖子,像sounds这个名字一样,可能不是部分抽水,所以也许这不再是真的了.但对于我自己来说,我会非常谨慎地在STA/GUI线程上做这样的事情.

总之,这不是我推荐的方法.我建议改用泛型-值-类型-约束接口方法.但是,如果您对所有将同步运行的代码有很强的了解,并且确保不会遇到上述情况,那么它将是可以接受的.

Csharp相关问答推荐

PredicateBuilder不是循环工作,而是手动工作

子组件:如何根据另一个组件的状态启用输入

.NET框架4.7.2项目如何引用.NET Core 2.2库?

如何使用Unity和MRTK3将手网添加到Hololens 2应用程序中

获取ASP.NET核心身份认证cookie名称

System.Text.Json数据化的C#类映射

在一个模拟上设置一个方法,该模拟具有一个参数,该参数是一个numc函数表达式

Int和uint相乘得到LONG?

从依赖项容器在.NET 8中的Program.cs文件中添加IOC

异步任务导致内存泄漏

在IAsyncEnumerable上先调用,然后跳过(1)可以吗?

有空容错运算符的对立面吗?

带有列表参数的表达式树

基于C#方法的EF核心过滤查询(缓冲与流)

错误:此版本的Visual Studio无法打开以下项目

并发表更新.EF核心交易

HttpClient,上传文件时实现进度

如何在JSON:API中定义的&过滤查询参数系列&标准的GET请求中传递多个相关参数?

当我在Git中暂存文件更改时,它们会消失

C#LINQ多行条件