迭代迭代器方法返回的任务

时间:2012-12-08 01:18:09

标签: .net vb.net iterator task-parallel-library

首先看一下这个简单的方法:

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

2 个答案:

答案 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