我在ASP.NET WebForms网站上有一个服务器端点击事件。在这种情况下,我调用一个方法,该方法又调用其异步伙伴方法,在通话中添加.Wait()
。
然后这个方法向下几级(即。,调用另一个异步方法,调用另一个异步方法,依此类推),最终在HttpClient对象上调用异步方法。在这一点上,线程似乎从兔子洞里消失了;该方法永远不会回电。
现在,我知道异步系列调用按预期工作,因为同样的代码也是从Web API控制器调用的(控制器方法调用第一个方法的异步版本,而不是同步'伙伴'方法),这是完全异步的,它会按预期返回。
所以基本上我有这样的东西,永远不会返回
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Class1.DoSomethingAsync.Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // never returns
}
我确实尝试在第一个异步方法上使用.ConfigureAwait(false)
,但没有任何成功。
我也有这个, 返回
Task<IHttpActionResult> MyWebApiMethod()
> await Class1.DoSomethingAsync()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // does return
}
我发现如果将其更改为以下内容,我可以使第一个版本正常工作:
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync()
}
但我不知道为什么。
任何人都可以解释调用
之间的区别Class1.DoSomethingAsync.Wait()
并致电
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
答案 0 :(得分:7)
我在我的博文Don't Block on Asynchronous Code和我在asynchronous best practices的MSDN文章中解释了这种行为。
线程似乎从兔子洞里消失了;该方法永远不会回电。
这是因为其中一个await
正在尝试在ASP.NET上下文中恢复,但是请求线程(使用该ASP.NET上下文)被阻塞,等待任务完成。这就是造成僵局的原因。
我确实尝试在第一个异步方法上使用.ConfigureAwait(false)但没有任何成功。
为了避免使用ConfigureAwait(false)
出现此死锁,必须将其应用于每个方法中的每个 await
。因此DoSomethingAsync
必须对每个await
使用它,DoSomethingAsync
调用的每个方法必须为每个await
使用它(例如,Authenticate
),每个方法都是await
方法调用必须对每个PostAsync
使用它(例如,HttpClient
)等。请注意,最后这里依赖于库代码,实际上Task.Run
已经错过了一些这些在过去。
如果我更改了[使用
Task.Run
],我发现我可以让第一个版本正常工作。
烨。 await
将在线程池线程上执行其委托,而不需要任何上下文。所以这就是没有死锁的原因:async void
没有尝试在ASP.NET上下文中恢复。
为什么不以正确的方式使用它。 async void btn_Click并等待Class1.DoSomethingAsync()?
不要将Task.Run与已经异步的方法一起使用,这是浪费线程。只需更改button_click eventhandler的签名
即可
这里的正确的答案:不要阻止异步代码。只需使用await
事件处理程序,然后使用 Between DateAdd("d",1,Now()) And DateAdd("d",-14,Now())
。
P.S。 ASP.NET Core不再具有ASP.NET上下文,因此您可以根据需要阻止,而不必担心死锁。但是你当然不应该这样做,因为它效率低下。