web-dev-qa-db-ja.com

リストに追加するParallel.ForEach

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

例えば:

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;
}

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

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

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

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

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

49
Haedrian

同時収集 を使用できます。

System.Collections.Concurrent名前空間は、複数のスレッドが同時にコレクションにアクセスするときはいつでも、System.Collections名前空間とSystem.Collections.Generic名前空間の対応する型の代わりに使用するいくつかのスレッドセーフコレクションクラスを提供します。

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

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

134
Mark Byers

コンカレントコレクションは、.Net 4の新機能です。新しい並列機能と連携するように設計されています。

。NET Framework 4の同時コレクション を参照してください:

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

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

22
arootbeer

コードを好む人向け:

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

    return results;
}
21

これは、PLINQのAsParallelおよびSelectManyを使用して簡潔に表現できます。

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