2016-05-04 8 views
4

私はConcurrentQueueという概念を中心に非常に基本的な質問がありました。キューはFIFOです。複数のスレッドがアクセスを開始すると、どのようにFIFOを保証するのですか? と仮定して、私はApple,Oranges,Lemon,PeachおよびApricotの順に追加しました。最初のTryTakeAppleを返す必要があります。しかし、複数のスレッドが独自のTryTake要求を出し始めるとどうなりますか?他のスレッドがAppleを返す前に、あるスレッドがLemonを返す可能性がありますか?キューが空になるまで他のアイテムも返されると仮定しています。しかし、これらのリターンはFIFOの基本原則を支配するでしょうか?ConcurrentQueue .Net:マルチスレッドコンシューマ

答えて

6

ConcurrentQueue自体の動作は、常にFIFOになります。我々はConcurrentQueueから項目を「返す」のスレッドについて話すとき

、我々はあなたがデキューされたものを観察することができます操作のいくつかの並べ替えを行ってデキュー項目の両方を必要とする操作の話をしています。アウトプットを出力している場合でも、そのアイテムを別のリストに追加している場合でも、検査するまでキューから取り出したアイテムは実際には分かりません。

キュー自体はFIFOですが、デキューされたアイテムを検査するなどの他のイベントが発生する順序を予測することはできません。アイテムはデキューされたFIFOになりますが、その順序でキューから出て来るものを観察することもできないこともあります。異なるスレッドは、キューから項目を削除したのとまったく同じ順序で検査または出力を実行しないことがあります。

つまり、FIFOで発生することになりますが、必ずしもそうでない場合もあります。処理されたアイテムの正確なシーケンスが重要である場合は、ConcurrentQueueから同時に読み取ることは望ましくありません。

これをテストしようとすると(私は何かを書こうとしています)、ほとんどの場合正確なFIFOシーケンスで処理されるアイテムが見つかるでしょう。


ここにコンソールアプリがあります。

  • には、シングルスレッドのConcurrentQueueに1~5000の数字を挿入します。
  • これらのアイテムのそれぞれをデキューして別のConcurrentQueueに移動するために並行操作を実行します。 これは「マルチスレッド消費者」です。
  • 2番目のキュー内の項目を読み込み(シングルスレッド、もう一度)、順不同の番号を報告します。

何度も私はそれを実行し、何も順不同です。しかし、約50%の時間で、順不同の数が報告されます。したがって、元のシーケンスで処理されるすべての数値に基づいている場合、ほとんどの場合、ほぼすべての数値が発生します。しかし、そうではありません。正確なシーケンスは気にせず、バグがあり、予測できない場合はうまくいきます。

結論は、マルチスレッド処理の正確なシーケンスには依存しません。

using System; 
using System.Collections.Concurrent; 
using System.Linq; 
using System.Threading.Tasks; 

namespace ConcurrentQueueExperiment 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var inputQueue = new ConcurrentQueue<int>(); 
      var outputQueue = new ConcurrentQueue<int>(); 
      Enumerable.Range(1,5000).ToList().ForEach(inputQueue.Enqueue); 
      while (inputQueue.Any()) 
      { 
       Task.Factory.StartNew(() => 
       { 
        int dequeued; 
        if (inputQueue.TryDequeue(out dequeued)) 
        { 
         outputQueue.Enqueue(dequeued); 
        } 
       }); 
      } 
      int output = 0; 
      var previous = 0; 
      while (outputQueue.TryDequeue(out output)) 
      { 
       if(output!=previous+1) 
        Console.WriteLine("Out of sequence: {0}, {1}", previous, output); 
       previous = output; 
      } 
      Console.WriteLine("Done!"); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

+1、特に「アイテムはFIFOからデキューされますが、その順序でキューから出て来るものを監視することはできないかもしれません」。 –

+0

多くのありがとうございます。それは間違いなく答えに役立ちます。しかし、そのFIFOがマルチスレッド化すると、どのように役立ちますか?マルチスレッドがアイテムを「レポートする」方法であれば、「TryTake」を順番に実行することもできますか?マルチスレッドの利点は、どのアイテムも同時に取り出して、最初のアイテムを取り出す必要があるだけです(キューの目的を破ることは確かですが、マルチスレッドとデキューを連続して実行するのは必ずしも手に入りません)。 – Kallol

+0

私はScottのコードをチェックし、デキューするTPL呼び出しを削除し、それを順次にしました。パフォーマンスを大幅に節約できます。 TPLでは、i7、クアッドコア、16GBマシンの50Kアイテムのデキューにはほぼ1000msかかりますが、デキューシーケンシャルを実行すると平均23-30ms以内になります。したがって、アイテムはFIFOで検索されるため、同時キューのマルチスレッド化はボトルネックになります。これは、私が助けを求めていた以前の質問、つまり並行キューのデキュー操作でのマルチスレッドの利点 – Kallol

関連する問題