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

问题描述 投票:-1回答:3

有2个意见,我的实验表明两者都有。

  1. 不。等待只是将控制权移交给来电者。
  2. 是。要执行的下一个语句是await之后的下一个语句。

考虑这段代码。

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之后,dim 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

已经完成了。

我其实想要相反的结果。 uniqueAccounts中的每个帐户都应该有自己的任务(或者线程,但await和sync实际上是单线程的吗?)。

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

我怎样才能做到这一点?

vb.net asynchronous async-await
3个回答
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
投票

Await不会阻塞线程并始终将控制传递给调用者,这就是它的目的。

这段代码

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

我其实想要相反的结果。 uniqueAccounts中的每个帐户都应该有自己的任务(或者线程,但await和sync实际上是单线程的吗?)。

对于你想要在一个单独的线程上完成的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

0
投票

我总结了两个答案。这就是我在await之后使启用按钮发生的操作

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的想法是一样的。但是,我用于下一个而不是选择。因此它更容易调试。

© www.soinside.com 2019 - 2024. All rights reserved.