1

先来看这个简单的方法:

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 调用(没关系)。

我的问题是一个简单的迭代将一次启动一个任务,因此每次迭代之间都会发生 3000 毫秒的延迟。

    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
4

2 回答 2

3

您可以调用ToList创建所有的Tasks。然后你可以使用For Each(或者Task.WhenAll如果你只是Awaiting 每一个)。

于 2012-12-08T02:51:07.560 回答
2

只是为斯蒂芬的答案添加一些解释:GetLongRunningTasks()返回一个惰性迭代器,它Task仅在您迭代它时创建 s 。在您的原始代码中,每次迭代都会创建一个Task,然后等待它完成,然后才开始另一个迭代,然后再开始另一个Task.

所以,你需要它来首先遍历整个集合以启动所有Tasks 并等待它们完成,只有当你拥有它们时。斯蒂芬的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-boundTask很可能不是最有效的选择,以有限的并行度运行它们是。为此,您可以使用SemaphoreSlim'sWaitAsnyc()或使用TPL Dataflow 中的ActionBlockwith MaxDegreeOfParallelismset。

于 2012-12-08T11:33:20.513 回答