为什么等待代码无限期冻结?

时间:2015-01-15 21:45:52

标签: .net vb.net asynchronous async-await

我有这段代码:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim t = DoStuffAsync()
    t.Wait()
    Debug.Print(t.Result)
End Sub

Private Async Function DoStuffAsync() As Task(Of String)
    Await Task.Delay(2000)
    Return "Stuff"
End Function

我知道这不是正确的做法。以下是我认为可能正确的方法:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim t = DoStuffAsync()
    Debug.Print(Await t)
End Sub

Private Async Function DoStuffAsync() As Task(Of String)
    Await Task.Delay(2000)
    Return "Stuff"
End Function

我只是想知道,当我运行第一个代码示例时,为什么它会在t.Wait()无限期挂起? 正好在该点执行代码时发生了什么,即什么代码是"运行"线程在t.Wait()时被阻止?

2 个答案:

答案 0 :(得分:6)

让我们首先解决问题:是的,你应该Await而不是Wait()结果。现在问你为什么会发生这种情况......

Stephen Cleary's blog post Don't Block on Async Code中,他解释了这是如何发生的。这是他的"会发生什么"演练,修改为您的方法:

  1. 顶级方法Button1_Click调用DoStuffAsync(在UI上下文中)。
  2. DoStuffAsync调用Task.Delay(仍在上下文中)。
  3. Task.Delay返回未完成的任务,表示时间尚未过去。
  4. DoStuffAsync等待Task返回的Task.Delay。捕获上下文,稍后将用于继续运行DoStuffAsync方法。 DoStuffAsync会返回未完成的Task,表示DoStuffAsync方法尚未完成。
  5. 顶级方法会同步阻止Task返回的DoStuffAsync。这会阻止上下文线程。
  6. ...最终,指定的时间将过去,Delay任务将完成。
  7. DoStuffAsync的延续现在已准备好运行,它等待上下文可用,以便它可以在上下文中执行。
  8. 死锁。顶级方法阻止上下文线程,等待DoStuffAsync完成,DoStuffAsync正在等待上下文空闲,以便它可以完成。
  9. 在这个UI上下文中,这也恰好是一个特定的线程,但这不一定是演员。例如,在ASP.NET中,可以在任何线程上运行的上下文,但一次只能在一个线程上运行。重要的是上下文被锁定,无论它实际运行的是什么线程。

答案 1 :(得分:1)

它陷入僵局。您正在UI线程上运行Task.Delay。然后阻止等待Task.Delay完成的UI线程。你是对的:你应该await Task