我有这段代码:
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()
时被阻止?
答案 0 :(得分:6)
让我们首先解决问题:是的,你应该Await
而不是Wait()
结果。现在问你为什么会发生这种情况......
在Stephen Cleary's blog post Don't Block on Async Code中,他解释了这是如何发生的。这是他的"会发生什么"演练,修改为您的方法:
Button1_Click
调用DoStuffAsync
(在UI上下文中)。DoStuffAsync
调用Task.Delay
(仍在上下文中)。Task.Delay
返回未完成的任务,表示时间尚未过去。DoStuffAsync
等待Task
返回的Task.Delay
。捕获上下文,稍后将用于继续运行DoStuffAsync
方法。 DoStuffAsync
会返回未完成的Task
,表示DoStuffAsync
方法尚未完成。Task
返回的DoStuffAsync
。这会阻止上下文线程。Delay
任务将完成。DoStuffAsync
的延续现在已准备好运行,它等待上下文可用,以便它可以在上下文中执行。DoStuffAsync
完成,DoStuffAsync
正在等待上下文空闲,以便它可以完成。在这个UI上下文中,这也恰好是一个特定的线程,但这不一定是演员。例如,在ASP.NET中,可以在任何线程上运行的上下文,但一次只能在一个线程上运行。重要的是上下文被锁定,无论它实际运行的是什么线程。
答案 1 :(得分:1)
它陷入僵局。您正在UI线程上运行Task.Delay
。然后阻止等待Task.Delay
完成的UI线程。你是对的:你应该await
Task
。