1. ホーム
  2. c#

[解決済み】非同期ラムダによる並列foreach

2022-04-07 20:17:23

質問

コレクションを並列に処理したいのですが、実装がうまくいかないので、ご教授願います。

C#でasyncとマークされたメソッドを、並列ループのラムダ内で呼び出したい場合に問題が発生します。例えば、以下のような感じです。

var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;

カウントが0になると問題が発生します。なぜなら、作成されたスレッドはすべて事実上単なるバックグラウンドスレッドであり Parallel.ForEach の呼び出しは完了を待ちません。asyncキーワードを削除すると、このメソッドは次のようになります。

var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, item =>
{
  // some pre stuff
  var responseTask = await GetData(item);
  responseTask.Wait();
  var response = responseTask.Result;
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;

これは動作しますが、await の巧妙な機能を完全に無効にしてしまうので、手動で例外処理を行う必要があります...。(簡潔さのために削除)。

を実装するにはどうすればよいのでしょうか? Parallel.ForEach ループで、ラムダ内で await キーワードを使用しますか?可能でしょうか?

Parallel.ForEachメソッドのプロトタイプは、Parallel.ForEachメソッドと同様に Action<T> をパラメータとした場合、非同期ラムダを待つようにしたい。

どのように解決するのですか?

単純な並列化だけなら、こうすればいいんです。

var bag = new ConcurrentBag<object>();
var tasks = myCollection.Select(async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
});
await Task.WhenAll(tasks);
var count = bag.Count;

もっと複雑なものが必要な場合は、以下をチェックしてください。 Stephen Toubの ForEachAsync ポスト .