等待阻塞线程还是传递给调用者,还是两者都传递?

时间:2019-04-25 05:19:43

标签: vb.net asynchronous async-await

有2种意见,我的实验表明这两种情况都会发生。

  1. 不。等待将控件移至调用方。
  2. 是的。下一条要执行的语句是等待后的下一条语句。

考虑此代码。

Public Shared Async Function getMarketDetailFromAllExchangesAsync() As Task
    For Each account In uniqueAccounts()
        Await account.Value.getMarketInfoAsync()
    Next
End Function

该代码调用

Public Overrides Async Function getMarketInfoAsync() As Task
    'readbalances()
    Dim json = Await CookieAwareWebClient.downloadString1Async("https://api.lbkex.com/v1/ticker.do?symbol=all")

    Dim ja = JArray.Parse(json)

    For Each jo In ja
        Dim symbol = jo("symbol").ToString
        Dim base1 = ""
...
        Dim bidaskTuple = Await getBidAskFromDepthArrayWithURLAsync("https://api.lbkex.com/v1/depth.do?symbol=" + symbol + "&size=1", "bids", "asks", CDbl(jo(LOW)), CDbl(jo(HIGH)))
        jo(BID) = bidaskTuple.Item1
        jo(ASK) = bidaskTuple.Item2
    Next

    typicalGetMarketInfoWithJsonStringAlreadyReady(ja.ToString, "", BID, ASK, HIGH, LOW, VOLUME, LAST, "https://www.lbank.info/exchange.html?asset={quote}&post={base}", "", BASE, QUOTE, "", 0, "")
    'getOrders()
End Function

之后

        Dim bidaskTuple = Await getBidAskFromDepthArrayWithURLAsync("https://api.lbkex.com/v1/depth.do?symbol=" + symbol + "&size=1", "bids", "asks", CDbl(jo(LOW)), CDbl(jo(HIGH)))

下一个调用的代码是

        jo(BID) = bidaskTuple.Item1

下一条语句是

但是,在这段代码中

Public Async Sub finRepeatOrderingSync()
    Dim i = Await finRepeatOrderingAsync()
    Dim b = i 'can't get it till we got i
End Sub

同一件事。在等待finRepeatOrderingAsync之后,尚未调用昏暗的b = i。

但是,运行finRepeatOrderingSync的代码已完成

此代码

Private Sub Button2_Click_1(sender As Object, e As EventArgs) Handles cmdGroupRepeat.Click
    startAndStopClickingButton(sender, Sub()
                                           finRepeatOrderingSync() ' this one never end
                                           Dim b = 1
                                       End Sub)
End Sub

完成。

我实际上想要相反的结果。唯一帐户中的每个帐户都应该有自己的任务(或线程,但是等待和同步实际上是单线程的吗?)。

另一方面,我想在finRepeatOrderingSync完成后重新启用控件中的所有按钮。

我该怎么做?

3 个答案:

答案 0 :(得分:2)

您的问题中有很多代码和很多讨论,这些问题实际上可以归结为该代码如何执行:

Public Async Function Foo() As Task
    Dim i = Await GetValueAsync()
    Dim b = i
End Function

Foo的呼叫将呼叫GetValueAsync,然后在Foo运行时返回到GetValueAsync的呼叫者,并且仅在Foo中继续结果的方法。

从逐步的角度来看,它与以下代码相同:

Public Sub Foo()
    Dim i = GetValue()
    Dim b = i
End Sub

从您最近的三段内容(似乎是您的问题中最重要的部分)中我了解到的是,您想同时执行所有getMarketInfoAsync调用,但仍等待所有这些调用。这很容易。

但是,首先,您的代码结构和命名约定不是您在.NET项目中通常看到的。我已经编写了一个更典型的场景,然后留给您翻译回您正在做的事情。

首先,我定义了以下简单的类:

Public Class MarketInfo
    Public Account As Account
End Class

Public Class Account
End Class

现在,我假设您有以下两种可用的方法并且可以正常工作:

Private Shared Function UniqueAccounts() As List(Of Account)
Public Shared Async Function GetMarketInfoAsync(account As Account) As Task(Of MarketInfo)

现在,您的原始方法已更新为适合这些更改,如下所示:

Public Shared Async Function GetMarketDetailFromAllExchangesAsync() As Task
    For Each account In UniqueAccounts()
        Await GetMarketInfoAsync(account)
    Next
End Function

根据我所说的操作发生情况,可以在某个地方Await GetMarketDetailFromAllExchangesAsync()处调用此代码,并且代码将针对每个account一对一执行,而不会阻塞调用线程。

您希望能够一次调用所有这些并返回所有结果。方法如下:

Public Shared Async Function GetMarketDetailFromAllExchangesAsync() As Task(Of MarketInfo())
    Return Await Task.WhenAll(UniqueAccounts().Select(Function (account) GetMarketInfoAsync(account)).ToArray())
End Function

现在您可以这样称呼:

Dim info As MarketInfo() = Await GetMarketDetailFromAllExchangesAsync()

答案 1 :(得分:1)

等待不会阻塞线程,而是始终将控制权传递给调用者,这就是目的。

  

此代码

Private Sub Button2_Click_1(sender As Object, e As EventArgs) Handles cmdGroupRepeat.Click
    startAndStopClickingButton(sender, Sub()
                                       finRepeatOrderingSync() ' this one never end
                                       Dim b = 1
                                   End Sub)
End Sub
  

完成。

您的事件处理程序是同步运行的,因为它从不等待任何东西,异步代码应始终由异步代码调用。看看https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

  

我实际上想要相反的结果。唯一帐户中的每个帐户都应该有自己的任务(或线程,但是等待和同步实际上是单线程的吗?)。

对于要在单独的线程上完成的CPU绑定工作,应使用Task.Run(),可在此处找到文档:https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netframework-4.8

  

另一方面,我想在finRepeatOrderingSync完成后重新启用控件中的所有按钮。

我该怎么做?

您可以通过禁用按钮,然后等待您想要完成的CPU绑定工作的结果,然后重新启用按钮来完成此操作:

Private Async Sub Button2_click(sender As Object, e As EventArgs) Handles Button2.click
    DisableButtons()
    Dim Result As Object = Await Task.Run(Function() SomeFunction)
    EnableButtons()

End Sub

答案 2 :(得分:0)

我正在总结两个答案。这就是我使启用按钮在等待之后发生的

Private Async Sub RepeatTheWholeThing_Click_1(sender As Object, e As EventArgs) Handles cmdGroupRepeat.Click
    Await startAndStopClickingButton2(sender, Async Function() As Task
                                                  Await finRepeatOrderingAsync() ' this one never end
                                                  Dim b = 1
                                              End Function)
End Sub

Public Async Function startAndStopClickingButton2(ByVal sender As Object, ByVal SomeSub As System.Func(Of Task)) As Task
    startClickingButton(sender)
    Await SomeSub.Invoke()
    stopClickingButton(sender)
End Function

这是我使帐户几乎同时加载的操作

Public Shared Async Function getMarketDetailFromAllExchangesAsync() As Task
    Dim taskList = New List(Of Task)
    For Each account In uniqueAccounts()
        Dim newtask = account.Value.getMarketInfoAsync()
        taskList.Add(newtask)
    Next
    Await Task.WhenAll(taskList.ToArray)

    Dim b = 1
End Function

该想法与@enigmativity想法相同。但是,我使用next代替select。因此,调试起来更容易。