有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完成后重新启用控件中的所有按钮。
我该怎么做?
答案 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。因此,调试起来更容易。