.net 为什么等待的代码会无限期冻结?

.net 为什么等待的代码会无限期冻结?,.net,vb.net,asynchronous,async-await,.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)

我有以下代码:

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()上被阻塞时,什么代码正在“运行”

这是死锁。您正在UI线程上运行
Task.Delay
。然后阻止UI线程等待
任务。延迟
完成。你是对的:你应该等待
任务

死锁。您正在UI线程上运行
Task.Delay
。然后阻止UI线程等待
任务。延迟
完成。你是对的:你应该等待
完成
任务

让我们先解决实际问题:是的,你应该等待而不是等待结果。现在谈谈你为什么会发生这种情况的问题

在本书中,他解释了这是如何发生的。以下是他的“发生了什么”演练,根据您的方法进行了修改:

  • 顶级方法
    按钮1\u单击
    调用
    dostufasync
    (在UI上下文中)
  • DoStuffAsync
    调用
    Task.Delay
    (仍在上下文中)
  • Task.Delay
    返回未完成的任务,表示时间尚未过去
  • dostufasync
    等待
    Task.Delay
    返回的
    Task
    。将捕获上下文,并将用于以后继续运行
    DoStuffAsync
    方法
    dostufasync
    返回未完成的
    任务
    ,表示
    dostufasync
    方法未完成
  • 顶级方法同步阻止由
    DoStuffAsync
    返回的
    任务。这将阻止上下文线程
  • …最终,指定的时间将过去,
    延迟
    任务将完成
  • DoStuffAsync
    的继续现在可以运行了,它等待上下文可用,以便可以在上下文中执行
  • 僵局。顶级方法正在阻止上下文线程,等待
    dostufasync
    完成,而
    dostufasync
    正在等待上下文释放以便完成

  • 在这个UI上下文中,这也恰好是一个特定的线程,但不需要强制转换。例如,在ASP.NET中,有一个上下文可以在任何线程上运行,但一次只能在一个线程上运行。重要的是上下文被锁定,不管它实际运行在什么线程上。

    让我们先解决实际问题:是的,您应该等待结果,而不是等待结果。现在谈谈你为什么会发生这种情况的问题

    在本书中,他解释了这是如何发生的。以下是他的“发生了什么”演练,根据您的方法进行了修改:

  • 顶级方法
    按钮1\u单击
    调用
    dostufasync
    (在UI上下文中)
  • DoStuffAsync
    调用
    Task.Delay
    (仍在上下文中)
  • Task.Delay
    返回未完成的任务,表示时间尚未过去
  • dostufasync
    等待
    Task.Delay
    返回的
    Task
    。将捕获上下文,并将用于以后继续运行
    DoStuffAsync
    方法
    dostufasync
    返回未完成的
    任务
    ,表示
    dostufasync
    方法未完成
  • 顶级方法同步阻止由
    DoStuffAsync
    返回的
    任务。这将阻止上下文线程
  • …最终,指定的时间将过去,
    延迟
    任务将完成
  • DoStuffAsync
    的继续现在可以运行了,它等待上下文可用,以便可以在上下文中执行
  • 僵局。顶级方法正在阻止上下文线程,等待
    dostufasync
    完成,而
    dostufasync
    正在等待上下文释放以便完成

  • 在这个UI上下文中,这也恰好是一个特定的线程,但不需要强制转换。例如,在ASP.NET中,有一个上下文可以在任何线程上运行,但一次只能在一个线程上运行。重要的是,上下文是锁定的,不管它实际运行在什么线程上。

    好的,所以我在阅读时开始感到困惑。看起来OP的示例代码在“我可以这样重写它:”下面会遇到与我的问题相同的情况,但是Eric Lippert在他的回答中说,“现在,如果你确实需要出于某种原因跟踪任务,那么你描述的技术是完全合理的。”我哪里搞混了?是
    Wait
    方法导致了问题。你刚才读的问题是关于
    async void
    方法的用例,并且没有调用
    Wait
    。好吧,所以我开始在阅读时感到困惑。看起来OP的示例代码在“我可以这样重写它:”下面会遇到与我的问题相同的情况,但是Eric Lippert在他的回答中说,“现在,如果你确实需要出于某种原因跟踪任务,那么你描述的技术是完全合理的。”我哪里搞混了?是
    Wait
    方法导致了问题。您阅读的问题是关于
    async void
    方法的用例,并且没有调用
    Wait
    。可能重复的可能重复感谢这一点,它提供了非常丰富的信息。最近我读了很多斯蒂芬·克利里的书,但我还没有读到这本书。谢谢你,这本书内容丰富。我已经读过了