首先看一下这个简单的方法:
Public Iterator Function GetLongRunningTasks(count As Long) As IEnumerable(Of Task)
For i = 1 To count
Yield Task.Delay(3000)
Next
End Function
此方法返回指定数量的任务,每个任务在启动后3秒完成。我们称之为在非常糟糕的网络连接上模拟网络API调用(不重要)。
我的问题是,一个简单的迭代将一次启动一个任务,因此每次迭代之间会发生3000ms的延迟。
For Each t In GetLongRunningTasks(50)
Await t
Next ' this takes ~150 seconds to complete (50x3000ms)
我想要做的是一次启动所有50个任务,然后进入foreach循环。最好坚持上面的例子,这样做的正确方法是什么?
修改
正如斯蒂芬所建议的,一种解决方案是迭代GetLongRunningTasks(50).ToList()
。也许这只是我,但我认为在阅读代码时使用ToList的原因并不明显。
我想知道以下片段是否完全相同?
Dim tasks As New List(Of Task)
tasks.AddRange(GetLongRunningTasks(50))
For Each t In tasks
Await t
Next
答案 0 :(得分:3)
您可以致电ToList
创建所有Task
。然后,您可以使用For Each
(或Task.WhenAll
,如果您只是Await
每个人。)
答案 1 :(得分:2)
只是为Stephen的回答添加一些解释:GetLongRunningTasks()
返回一个惰性迭代器,只有在迭代它时才会创建Task
。在原始代码中,每次迭代都会创建一个Task
,然后等待它完成,然后才开始另一次迭代,从而启动另一个Task
。
那么,你需要它首先迭代整个集合来启动所有Task
并等待它们只有你拥有它们才能完成。斯蒂芬的ToList()
建议确实如此,你的AddRange()
也会这样做。
如果您仍然不清楚,也许还有一种方法可以帮助您:
Dim tasks As New List(Of Task)
For Each t in GetLongRunningTasks(50)
tasks.Add(t)
Next
For Each t In tasks
Await t
Next
此外,启动大量IO绑定Task
最有可能不是最有效的选项,以有限的并行度运行它们。为此,您可以使用SemaphoreSlim
WaitAsnyc()
,或使用ActionBlock
与TPL数据流设置的MaxDegreeOfParallelism
。