2017-01-05 11 views
6

私の質問は少し理論的です。Parallel.Forをこのように使用した場合、List<object>がスレッドセーフであるかどうかを知りたいと思います。以下を参照してください:C#並列ループローカル変数スレッドセーフ情報

public static List<uint> AllPrimesParallelAggregated(uint from, uint to) 
     { 
      List<uint> result = new List<uint>(); 
      Parallel.For((int)from, (int)to, 
       () => new List<uint>(), // Local state initializer 
       (i, pls, local) =>  // Loop body 
       { 
        if (IsPrime((uint)i)) 
        { 
         local.Add((uint)i); 
        } 
        return local; 
       }, 
       local =>    // Local to global state combiner 
       { 
        lock (result) 
        { 
         result.AddRange(local); 
        } 
       }); 
      return result; 
     } 

localはスレッドセーフですか?私が正しいデータをresultのデータなしで持っているかどうかは、私が通常のループをしているので、複数のスレッドのために変更されていますか?

注:私はリストの順序について心配していません。私はリストとデータの長さについて知りたい。

+0

'List'は並列スレッドセーフではありません、特にあなたの意図です。 –

+0

したがって、私の 'result'リストの意味は、通常のループから取得できるデータと同じではありませんか? – Saadi

+0

これは意味です..... 'List'はスレッドセーフではありません!! –

答えて

6

このソリューションはスレッドセーフですか?技術的にはい、現実にはありません。

スレッドセーフListの非常に概念は、(キューまたは袋ではなく)、より厳密に、リストはために関して安全であるということである、または、インデックス、どのキーが存在しないため、昇順以外の整数。パラレルな世界では、それはあなたがそれについて考えるとき、無意味な概念のようなものです。そのため、System.Collections.Concurrent名前空間にはConcurrentBagConcurrentQueueが含まれていますが、ConcurrentListは含まれていません。

リストのスレッドの安全性について質問しているので、私はあなたのソフトウェアが昇順のリストを生成することを前提としています。その場合、いいえ、あなたのソリューションは動作しません。コードは技術的にスレッドセーフですが、スレッドは任意の順序で終了し、変数resultはソートされなくなります。

並列計算を使用する場合は、結果をバッグに格納し、すべてのスレッドが終了したらバッグをソートして順序付きリストを生成する必要があります。それ以外の場合は、計算を連続して実行する必要があります。

とにかくバッグを使用する必要があるので、ConcurrentBagを使用すると、lock{}ステートメントで気にする必要はありません。

+0

すてきで簡潔な答えをありがとう。 – Saadi

+0

はい。この種のもののためにPLINQを好まないと、おそらく私の解決策では 'ConcurrentBag'が機能するでしょう。 – spender

2

List<T>はスレッドセーフではありません。あなたがそれを使用している方法では、スレッドの安全性は必要ありません。複数のスレッドから同時にリソースにアクセスする場合は、スレッドの安全性が必要です。あなたはローカルリストに取り組んでいるので、あなたはそれをしていません。

最後に、ローカルリストの内容をresult変数に追加します。この操作にはlockを使用しているため、そのブロック内でスレッドセーフです。

解決策はおそらく大丈夫です。

+0

ありがとうありがとう、簡潔な答えです。 – Saadi

3

リストスレッドセーフではありません。現在のアルゴリズムは動作します。それは他の答えとコメントで説明されていたからです。

これは<paramref name="localInit"/>デリゲートはループの実行に参加し、それらのスレッドごとに初期ローカルの状態を返します各スレッドに一度呼び出さあるFor.Parallel

のlocalInitについての説明です。これらの初期状態は、IMOあなたのループ内で不必要な複雑さを追加している最初の


に渡されます。代わりにConcurrentBagを使用します。これは設計上スレッドセーフです。

ConcurrentBag<uint> result = new ConcurrentBag<uint>(); 
Parallel.For((long) from, (long) to, 
    (i, PLS) => 
    { 
     if (IsPrime((uint)i)) 
     { 
      result.Add((uint)i); // this is thread safe. don't worry 
     } 
    }); 
return result.OrderBy(I => I).ToList(); // order if that matters 

See concurrent bag here

ConcurrentBagのすべてのパブリック及び保護されたメンバーは、スレッドセーフであり、複数のスレッドから同時に使用することができます。

+0

あなたの答えをありがとうが、私の質問は誰もがリストはスレッドセーフではないと言っている。 'result.Add((uint)i);'はスレッドセーフです。 – Saadi

+0

これはConcurrentBagです。リストではありません。 ConcurrentBagはこの目的のために設計されています。 –

関連する問題