在异步方法上调用.Wait()和Task.Run()之间的区别.Wait()

时间:2016-11-25 17:40:32

标签: c# asp.net asynchronous async-await

我在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()

1 个答案:

答案 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上下文,因此您可以根据需要阻止,而不必担心死锁。但是你当然不应该这样做,因为它效率低下。

相关问题