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呼び出しのシミュレーションと呼びましょう(重要ではありません)。

私の問題は、単純な反復で一度に1つずつタスクが開始されるため、各反復の間に3000ミリ秒の遅延が発生することです。

    For Each t In GetLongRunningTasks(50)
        Await t
    Next ' this takes ~150 seconds to complete (50x3000ms)

私がやりたいのは、50のタスクすべてを一度に開始し、その後foreachループに入るということです。できれば上記の例に固​​執して、これを行うための適切な方法は何ですか?

編集

Stephenが提案したように、1つの解決策はを繰り返すこと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て、すべてを作成できますTask。次に、を使用できますFor Each(または、それぞれを使用しTask.WhenAllている場合Await)。

于 2012-12-08T02:51:07.560 に答える
2

スティーブンの答えにいくつかの説明を追加するだけです。GetLongRunningTasks()怠惰なイテレータを返します。これは、Task反復したときにのみsを作成します。元のコードでは、各反復で単一のが作成Taskされ、それが完了するのを待ってから、別の反復が開始され、別のが開始されますTask

したがって、最初にコレクション全体を反復処理してすべてのを開始し、すべてが揃ったTaskときにのみ完了するのを待つために必要なものです。スティーブンのToList()提案はまさにそれをします、そしてあなたは同じことをするAddRange()でしょう。

それでもはっきりしない場合は、同じことを行うもう1つの方法が役立つでしょう。

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'sを使用するか、 TPLDataflowのsetでWaitAsnyc()使用ActionBlockします。MaxDegreeOfParallelism

于 2012-12-08T11:33:20.513 に答える