有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之后,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完成后启用我控件中的所有按钮。
我怎样才能做到这一点?
您的问题中有很多代码和大量讨论,这些讨论实际上可以归结为此代码的执行方式:
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()
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
我总结了两个答案。这就是我在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的想法是一样的。但是,我用于下一个而不是选择。因此它更容易调试。