等待导致主线程持有?

时间:2014-12-17 20:24:23

标签: vb.net asynchronous

请参阅以下代码,我已从以下网页进行了改编:http://www.codeguru.com/csharp/csharp/introduction-to-async-and-await-keywords-in-c-5.0.htm

Public Class Form1

    Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim program As New Program()
        Await program.PrintSumAsync()
        MsgBox("got here 1")
    End Sub
End Class

Class Program

    Public Async Function PrintSumAsync() As Task
        Dim value1 As Integer = Await GetValueAsync()
        Dim value2 As Integer = Await GetValueAsync()

        Console.WriteLine("Sum of two random numbers is: {0}", value1 + value2)
    End Function

    Private Async Function GetValueAsync() As Task(Of Integer)
        System.Threading.Thread.Sleep(5000)
        Dim random As Integer = ComputeValue()
        Return random
    End Function


    Private Function ComputeValue() As Integer
        MsgBox("got here 2")
        Return New Random().[Next](1, 1000)

    End Function
End Class

我在GetValueAsync中添加了对Sleep的调用,因此需要一段时间才能完成。

我希望代码在msgbox2之前到达msgbox1(在这里得到1)(到这里2)。等待似乎停止了主线程。我错过了什么?我对newish await关键字没有任何经验。我最近从.net 3.5升级到.NET 4.5.2。

更新 根据戴维斯的回答,我编写了如下代码:

Public Class Form1

    Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        test()
        MsgBox("got here 1")
    End Sub

    Public Async Sub test()
        Dim program As New Program()
        Await program.PrintSumAsync()
        'Dim task As Task = program.PrintSumAsync()
        MsgBox("got here 2")
    End Sub
End Class

Class Program

    Public Async Function PrintSumAsync() As Task
        Dim value1 As Integer = Await GetValueAsync()
        Dim value2 As Integer = Await GetValueAsync()

        Console.WriteLine("Sum of two random numbers is: {0}", value1 + value2)
    End Function

    Private Async Function GetValueAsync() As Task(Of Integer)
        Try
            Await Task.Delay(5000)
            Dim random As Integer = ComputeValue()
            Return random
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Function


    Private Function ComputeValue() As Integer
        MsgBox("got here 2")
        Return New Random().[Next](1, 1000)
    End Function
End Class

如何在消息框2出现之前停止主线程完成?

2 个答案:

答案 0 :(得分:4)

Await没有停止主线程,你是:

System.Threading.Thread.Sleep(5000)

此方法中没有发生异步

Private Async Function GetValueAsync() As Task(Of Integer)
    System.Threading.Thread.Sleep(5000)
    Dim random As Integer = ComputeValue()
    Return random
End Function

(我很惊讶编译器没有抱怨它。它与C#有关。)因此,即使方法签名和使用代码都使用AsyncAwait关键字进行修饰,代码实际上并不是异步的。但是,如果等待该方法中的异步操作,可以。例如:

Private Async Function GetValueAsync() As Task(Of Integer)
    Await Task.Delay(5000)
    Dim random As Integer = ComputeValue()
    Return random
End Function

这将使整个操作堆栈异步,充分利用新关键字。

但请注意,观察到的结果可能仍然相同。看看顶级方法:

Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim program As New Program()
    Await program.PrintSumAsync()
    MsgBox("got here 1")
End Sub

在等待第二行直到完成之后,该方法中的代码仍然不会执行第三行。 非常顶级(调用此处理程序的UI)将继续而不等待方法,但此方法将等待其异步操作完成后再继续。

为了获得您正在寻找的结果,您需要捕获Task而不是等待它。像这样:

Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim program As New Program()
    Dim task As Task = program.PrintSumAsync()
    MsgBox("got here 1")
    ' do something with the task object.
    ' await it, provide it with a callback function for when it completes, etc.
End Sub

AsyncAwait关键字不一定会在新线程上发生函数。对于大多数意图和目的,您通常可以将它们视为简单的句法简写,用于在ContinueWith() Task对象上包装方法的一部分。在任何给定的Async方法中,都需要Await来放置ContinueWith()。如果其中没有Await,则Async方法不是异步的。

答案 1 :(得分:1)

只是因为粗暴和胫骨,玩这个。在我看来,获取随机值的过程应该在 parallel

中运行
Public Class Form1

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Button1.Enabled = False
        Label1.Text = DateTime.Now
        Label2.Text = ""

        Await test()

        Label2.Text = DateTime.Now
        Button1.Enabled = True
    End Sub

    Public Async Function test() As Task
        Dim program As New Program()
        Await Task.Run(New Action(AddressOf program.PrintSum))
    End Function

End Class

Class Program

    Private R As New Random

    Public Sub PrintSum()
        ' get some random values IN PARALLEL and wait for them all to finish
        Dim tasks As New List(Of Task(Of Integer))
        For i As Integer = 1 To 5
            tasks.Add(Task(Of Integer).Factory.StartNew(Function() GetValue()))
        Next
        Task.WaitAll(tasks.ToArray)
        Dim sum As Integer = 0
        For i As Integer = 0 To tasks.Count - 1
            sum = sum + tasks(i).Result
        Next
        Console.WriteLine("Sum of the random numbers is: " & sum)
    End Sub

    Private Function GetValue() As Integer
        Console.WriteLine("Getting value...")
        System.Threading.Thread.Sleep(R.Next(3000, 10001)) ' random 3 to 10 second delay
        Return ComputeValue()
    End Function

    Private Function ComputeValue() As Integer
        Dim i As Integer = R.Next(1, 1000)
        Console.WriteLine("Value selected: " & i)
        Return i
    End Function

End Class