リストに追加するParallel.ForEach


80

(ネットワークによって)リモートサイトに接続し、汎用リストを返す複数の関数を実行しようとしています。しかし、私はそれらを同時に実行したいと思います。

例えば:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

私が見ているように、「結果」への複数の挿入が同時に発生する可能性があります...これによりアプリケーションがクラッシュする可能性があります。

どうすればこれを回避できますか?


どの.NETバージョンを使用していますか?
sll 2011年

4
少なくとも.Net4である必要があります。そこでParallelが導入されました。
マットミルズ

回答:


58
//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

基本的に、ロックとは、1つのスレッドだけが同時にそのクリティカルセクションにアクセスできることを意味します。


1
しかし、これらの結果が追加されている間に、別のプロバイダーからの結果を追加しようとするとどうなりますか?可能になるまで失敗または待機しますか?
shaharmor 2011年

4
ロックがある場合、スレッドはロックを取得できるまで待機します。
Haedrian 2011年

つまり、基本的には次のように言っています。!results.isLockedまで待ち、自由にロックして書き込みますか?
shaharmor 2011年

8
マイナーなポイント: thisロックオブジェクトの最も安全な選択ではありません。特別なプライベートオブジェクトを使用することをお勧めします:lock(resultsLock)
ヘンクホルターマン2011年

2
locksただし、全体的な実行時間が遅くなる可能性があります。同時収集はそれを回避する方がよいようです
Piotr Kula

155

並行コレクションを使用できます。

System.Collections.Concurrent名前空間は、対応するタイプの代わりに使用しなければならないいくつかのスレッドセーフなコレクションクラスを提供System.Collections し、System.Collections.Generic複数のスレッドが同時にコレクションにアクセスされるたびに名前空間を。

たとえばConcurrentBag、アイテムが追加される順序が保証されていないため、使用できます。

スレッドセーフな、順序付けられていないオブジェクトのコレクションを表します。


はい、これが実際の答えです。並行コレクションを使用すると、(一般的に)パフォーマンスが向上します。
lkg 2017年

32

コードを好む人のために:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

ループを使用する必要があります: foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
AnthonyMcGrath20年

25

並行コレクションは.Net4の新機能です。これらは、新しい並列機能で動作するように設計されています。

.NET Framework4の並行コレクションを参照してください。

.NET 4より前は、複数のスレッドが単一の共有コレクションにアクセスしている可能性がある場合は、独自の同期メカニズムを提供する必要がありました。コレクションをロックする必要がありました...

... System.Collections.Concurrent [.NET 4で追加]の[新しい]クラスとインターフェイスは、スレッド間で共有されるデータを含む[...]マルチスレッドプログラミングの問題に対して一貫した実装を提供します。


14

これは、PLINQAsParallelSelectMany:を使用して簡潔に表現できます。

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}

linq selectManyは素晴らしいですが、悲しいことにlinqは通常のforeachよりも遅いです。:(
Kugan Kumar

6
微最適化しないでください。SearchTitleリモートサイトに接続することを意味するOP 。そのレイテンシーは、LINQとforeachの違いよりも数桁遅くなります。
ダグラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.