4

私はTask(それらの多く、約400)のセットを持っています:

IEnumerable<Task> tasks = ...

それらをすべて同時に実行してから、それぞれを待ちたいと思います。このコードを使用してタスクを実行します。

Task.Run(async () => { ... });

各タスクは非同期メソッド自体を実行するためasync、ラムダにキーワードが必要です。これらのネストされたタスクの中にはHTTP、送信されるリクエストとHTTP受信されるレスポンスがあることで有名です。

すべてのタスクが完了するのを待つために、2 つの異なる方法を試しました。

await Task.WhenAll(tasks);

foreach (var task in tasks)
{
    await task;
}

これは、先験的に、私にはまったく同じように見えます(しかし、もちろんそうではないようです。それ以外の場合は、そもそもここに投稿しません...)。

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll最初の方法はタスクの実行を高速化しますが、出力ウィンドウにはこのようなものがたくさんあります。さらに、一部のタスクは、呼び出しWaitingForActivationの状態のままです。await Task.WhenAll()

2 番目の方法は遅く、タスクが同時に実行されていないように見えますHTTP(タスクを待機する最初の方法では、ほぼ同時に応答が返されます)。また、ループfirst chance exceptionを使用して各タスクを待機すると、出力ウィンドウに何も表示されず、ループ後の状態にあるforeachタスクはありません。WaitingForActivation

一連のタスクを待機する「最良の」方法はWhenAll()(少なくとも読みやすさのために) 使用することだと理解していますが、なぜこれら 2 つのメソッドの動作が異なるのでしょうか? どうすればこの問題を克服できますか? 理想的には、タスクを高速に実行し、すべてが終了したことを確認します (ラムダにサーバーエラーを処理するブロックがあり、誰かが尋ねる前にtry catch finallyを忘れていませんでした...)。if(httpClient != null) httpClient.Dispose()finally

どんなヒントでも大歓迎です!

編集

さて、私は別のことを試しました。追加した:

.ContinueWith(x => System.Diagnostics.Debug.WriteLine("#### ENDED = " + index)));

各タスクにindexは、Task. foreachループを使用すると、次のようになります。

#### ENDED = 0
#### ENDED = 1
#### ENDED = 2
#### ENDED = 3
#### ENDED = 4
...

を使用するWhenAll()と、次のようになります。

#### ENDED = 1
#### ENDED = 3
#### ENDED = 0
#### ENDED = 4
#### ENDED = 8
...

したがって、ループを使用すると、すべてのタスクが同期的に実行されます...これは、システムがアルゴリズムによってまったくストレスを受けていないため、出力ウィンドウforeachに何も表示されない理由を説明している可能性があります。First Chance Exception

EDIT2:

サンプルコード : http://pastebin.com/5bMWicD4

ここで利用可能な公共サービスを使用します: http://timezonedb.com/

4

2 に答える 2

5

2 つの試みはまったく異なります。

最初の試行は、すべてのタスクが完了するのを待ってから続行されます。すべてのタスクが完了した後にのみスローされます。結果の順序は不定であり、どのタスクが最初に終了するかによって異なります

2番目は、タスク配列に配置された順序で、各タスクを1つずつ待機しますが、これはもちろん、あなたが望むものではなく、かなり遅いです。タスクが 1 つでも失敗すると、例外で待機を中止します。他のタスクの結果は失われます。

一部のタスクは他のタスクよりも早く終了するため、タスクを同期的に実行するのとまったく同じではありませんが、一度にすべてのタスクを確認する必要があります。

Task.WhenAllここで、は単独ではブロックしないことに注意してください。他のすべてのタスクが終了したときに終了する Task を返します。呼び出すawait Task.WhenAllことで、そのタスクの完了を待ちます。そのタスクのステータスをチェックして、1 つ以上のサブタスクが失敗したかキャンセルされたかを確認したり、ContinueWith を呼び出して結果を処理したりできます。

await Task.WhenAllすべてのタスクが完了するまでブロックする代わりに、Task.WaitAll を呼び出すか、少なくとも 1 つのタスクがキャンセルまたは中止されることもできます。これは 2 回目の試行と似ていますが、すべてのタスクを 1 つずつ待機する必要はありません。

多くの例外があるという事実は、あなたが待っている方法とは何の関係もありません。同じドメイン (アドレス) に対して一度に確立できる HTTP 接続の数には制限があり、タイムアウト エラー (通常は接続制限が原因) やその他のネットワーク関連の問題が発生する可能性があります。

ただし、受け取る例外の種類は、または を呼び出すかどうかによって影響を受けawait Task.WhenAllますTask.WaitAllこの投稿では問題について説明していますが、要するに、Task.WaitAllすべての例外を収集して AggregateException をスローし、await Task.WhenAllそのうちの 1 つだけを返します。

ところで、SocketException で受け取るメッセージは何ですか?

于 2013-07-11T10:00:42.000 に答える
4

あなたのコードの振る舞いは とは何の関係もありませんawait。sのコレクションを反復する方法が原因ですTask。ほとんどの LINQ メソッドは遅延型です。つまり、実際にコードを実行するのは、反復処理を行う場合のみです。

したがって、このコードはTask、前のコードが完了した後にのみ開始されます。

foreach (var task in tasks)
{
    await task;
}

しかし、このコードはそれらすべてを一度に開始します:

foreach (var task in tasks.ToList())
{
    await task;
}

は内部的にTask.WhenAll()同等のToList()処理を行うため、上記の 2 番目のスニペットと同じ動作が得られます。

于 2013-07-12T09:51:37.900 に答える