C# 在异步方法上调用.Wait()和Task.Run().Wait()之间的区别
我在ASP.NET WebForms网站上有一个服务器端单击事件。在本例中,我调用一个方法,该方法反过来调用其异步伙伴方法,在调用中添加C# 在异步方法上调用.Wait()和Task.Run().Wait()之间的区别,c#,asp.net,asynchronous,async-await,C#,Asp.net,Asynchronous,Async Await,我在ASP.NET WebForms网站上有一个服务器端单击事件。在本例中,我调用一个方法,该方法反过来调用其异步伙伴方法,在调用中添加.Wait() 然后,该方法向下几个级别(即调用另一个异步方法,该方法调用另一个异步方法,等等),并最终在HttpClient对象上调用一个异步方法。在这一点上,线似乎消失在兔子洞里;该方法从不回调 现在,我知道异步调用系列可以按预期工作,因为同样的代码也可以从Web API控制器调用(控制器方法调用第一个方法的异步版本,而不是同步的“partner”方法),该
.Wait()
然后,该方法向下几个级别(即调用另一个异步方法,该方法调用另一个异步方法,等等),并最终在HttpClient对象上调用一个异步方法。在这一点上,线似乎消失在兔子洞里;该方法从不回调
现在,我知道异步调用系列可以按预期工作,因为同样的代码也可以从Web API控制器调用(控制器方法调用第一个方法的异步版本,而不是同步的“partner”方法),该方法是完全异步的,并且它会按预期返回
所以基本上我有这样的东西,永远不会回来
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Class1.DoSomethingAsync.Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // never returns
}
我确实尝试在第一个异步方法上使用.ConfigureAwait(false)
,但没有成功
我也有这个,它会返回
但我不知道为什么
有人能解释一下打电话和打电话的区别吗
Class1.DoSomethingAsync.Wait()
和呼唤
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
我在我的博客文章和MSDN上的文章中解释了这种行为 线似乎从兔子洞里消失了;该方法从不回调 发生这种情况的原因是,
wait
s试图在ASP.NET上下文上恢复,但请求线程(使用该ASP.NET上下文)在等待任务完成时被阻止。这就是导致死锁的原因
我确实尝试在第一个异步方法上使用.ConfigureAwait(false),但没有成功
为了使用ConfigureAwait(false)
避免此死锁,必须将其应用于调用的每个方法中的每个await
。因此DoSomethingAsync
必须对每个wait
使用它,每个DoSomethingAsync
调用的方法都必须对每个wait
使用它(例如,Authenticate
),这些方法调用的每个方法都必须对每个wait
使用它(例如,PostAsync
),等等。请注意,在这里的末尾,您依赖于库代码,事实上,HttpClient
在过去错过了其中一些
我发现,如果我将第一个版本更改为[使用Task.Run
],则可以使其正常工作
是的Task.Run
将在没有任何上下文的线程池线程上执行其委托。这就是为什么没有死锁的原因:没有一个wait
s尝试在ASP.NET上下文中恢复
你为什么不用正确的方法呢。异步无效btn_单击并等待Class1.DoSomethingAsync() 不要使用任务。使用已经异步的方法运行,这是对线程的浪费。只需更改按钮的签名即可单击eventhandler 正确的答案是:不要阻塞异步代码。只需使用
async void
事件处理程序,并使用wait
即可
p.S.ASP.NET核心不再具有ASP.NET上下文,因此您可以根据需要进行阻止,而无需担心死锁。当然,你还是不应该这样做,因为这样做效率很低。为什么不正确使用它呢
async void btn_单击
并等待Class1.DoSomethingAsync()?不要使用任务。使用已经异步的方法运行
,这是对线程的浪费。只需将按钮的签名更改为@user3185569suggested@user3185569-我没有使用它,因为我不知道我可以在服务器端WebForms事件上使用async
。谢谢你向我指出这一点。我对正确答案有部分不同意见。。。有时您需要从普通的旧异步代码调用异步方法,这种代码使用线程、锁和信号量,或者在一个很长的堆栈中,您不想让async Task
病毒一直侵扰到根。@LouisSomers:可以从同步代码调用异步代码,但没有一种解决方案可以在所有情况下都有效。我认为你评论的关键是“不要”——几乎总是可以用正确的方式来做;只是人们不想(或者,更常见的情况是,企业不会优先考虑工作)。
Class1.DoSomethingAsync.Wait()
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()