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

awj*_*awj 2 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
                  }
Run Code Online (Sandbox Code Playgroud)

我尝试使用.ConfigureAwait(false)第一个异步方法但没有任何成功.

我也有这个,它确实回来了

Task<IHttpActionResult> MyWebApiMethod()
    > await Class1.DoSomethingAsync()
        ...
            > await ClassN.Authenticate()
              {
                await myHttpClient.PostAsync()  // does return
              }
Run Code Online (Sandbox Code Playgroud)

我发现如果将其更改为以下内容,我可以使第一个版本正常工作:

protected void btn_Click(object sender, EventArgs e)
    > Class1.DoSomething()
        > Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
            ...
                > await ClassN.Authenticate()
                  {
                    await myHttpClient.PostAsync()
                  }
Run Code Online (Sandbox Code Playgroud)

但我不知道为什么.

任何人都可以解释呼叫之间的区别

Class1.DoSomethingAsync.Wait()
Run Code Online (Sandbox Code Playgroud)

并打电话

Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 7

我在我的博客文章Do not Block on Asynchronous Code和我的MSDN关于异步最佳实践的文章中解释了这种行为.

线程似乎从兔子洞里消失了; 该方法永远不会回电.

发生这种情况是因为其中一个awaits正在尝试在ASP.NET上下文中继续,但是请求线程(使用该ASP.NET上下文)被阻止等待任务完成.这就是导致僵局的原因.

我确实尝试在第一个异步方法上使用.ConfigureAwait(false)但没有任何成功.

为了避免这种僵局使用ConfigureAwait(false),必须应用到每一个 await每一个调用的方法.因此DoSomethingAsync必须为每await一个使用它,每个DoSomethingAsync调用的方法必须为每个await(例如Authenticate)使用它,这些方法调用的每个方法必须为每个await(例如PostAsync)等使用它.注意,最后在这里,你依赖于库代码,实际上HttpClient已经错过了过去的一些.

我发现如果我改变它[使用Task.Run] 我可以使第一个版本工作.

对.Task.Run将在没有任何上下文的线程池线程上执行其委托.所以这就是没有死锁的原因:没有一个await尝试在ASP.NET上下文中恢复.

你为什么不以正确的方式使用它.async void btn_Click并等待Class1.DoSomethingAsync()?

不要将Task.Run与已经异步的方法一起使用,这是浪费线程.只需更改button_click eventhandler的签名即可

这是正确的答案:不要阻止异步代码.只需使用async void事件处理程序并使用await.

PS ASP.NET Core不再具有ASP.NET上下文,因此您可以根据需要阻止,而不必担心死锁.但是你当然不应该这样做,因为它效率低下.