2009-09-03 6 views
7

次のように単純な1プロデューサ/ 2コンシューマコードがありますが、出力にはC2だけが消費されていることが示されています。私のコードにバグはありますか?C#プロデューサ/コンシューマパターン

class Program 
{ 
    static void Main(string[] args) 
    { 
     Object lockObj = new object(); 
     Queue<string> queue = new Queue<string>(); 
     Producer p = new Producer(queue, lockObj); 
     Consumer c1 = new Consumer(queue, lockObj, "c1"); 
     Consumer c2 = new Consumer(queue, lockObj, "c2"); 

     Thread t1 = new Thread(c1.consume); 
     Thread t2 = new Thread(c2.consume); 
     t1.Start(); 
     t2.Start(); 

     Thread t = new Thread(p.produce); 
     t.Start(); 

     Console.ReadLine(); 
    } 
} 
public class Producer 
{ 
    Queue<string> queue; 
    Object lockObject; 
    static int seq = 0; 
    public Producer(Queue<string> queue, Object lockObject) 
    { 
     this.queue = queue; 
     this.lockObject = lockObject; 
    } 

    public void produce() 
    { 
     while(seq++ <15) //just testinng 15 items 
     { 
      lock (lockObject) 
      { 
       string item = "item" + seq; 
       queue.Enqueue(item); 
       Console.WriteLine("Producing {0}", item); 
       if (queue.Count == 1) 
       { // first 
        Monitor.PulseAll(lockObject); 
       } 
      } 
     } 
    } 

} 

public class Consumer 
{ 
    Queue<string> queue; 
    Object lockObject; 
    string name; 
    public Consumer(Queue<string> queue, Object lockObject, string name) 
    { 
     this.queue = queue; 
     this.lockObject = lockObject; 
     this.name = name; 
    } 

    public void consume() 
    { 
     string item; 
     while (true) 
     { 
      lock (lockObject) 
      { 
       if (queue.Count == 0) 
       { 
        Monitor.Wait(lockObject); 
        continue; 
       } 
       item = queue.Dequeue(); 
       Console.WriteLine(" {0} Consuming {1}", name, item); 
      } 
     } 
    } 
} 

出力は:テストのため

Producing item1 
c2 Consuming item1 

Producing item2 
c2 Consuming item2 

Producing item3 
c2 Consuming item3 

Producing item4 
c2 Consuming item4 

Producing item5 
c2 Consuming item5 

Producing item6 
c2 Consuming item6 

Producing item7 
c2 Consuming item7 

Producing item8 
c2 Consuming item8 

Producing item9 
c2 Consuming item9 

Producing item10 
c2 Consuming item10 

Producing item11 
c2 Consuming item11 

Producing item12 
c2 Consuming item12 

Producing item13 
c2 Consuming item13 

Producing item14 
c2 Consuming item14 

Producing item15 
c2 Consuming item15 
+0

達成しようとしていることを正確に説明できますか?あなたの例は少し工夫されているので、私はあなたがする必要がある文脈から判断することはできません。 – jrista

+0

こんにちはサウスサウス州、[このプロデューサー - コンシューマーの例](https://stackoverflow.com/questions/733793/implementing-the-producer-consumer-pattern-in-c-sharp/47179576#47179576)をご覧ください。それはあなたを助けるかもしれません。 –

答えて

4

、消費者コード内の時間遅延を追加してみ。 「消費」が非常に速いため、ある消費者スレッドが他の消費者スレッドがチャンスを得る前にキ​​ューを空にすることがあり得る。

(編集)

Iは

のThread.sleep(500)を追加して、疑われるように、消費者スレッド内で(いくらかの長い処理をシミュレートするため)、両方のスレッドが利用される結果となる。別ウィンドウ(タブ)の大きな表示で見る

2

キューの数が1に等しい場合、プロデューサはMonitor.PulseAllのみを呼び出します。これは、プロデューサによって実体が何も行われていないためあまり頻繁ではありません。つまり、ゲートを介した最初の消費スレッドは、最初の項目では、2番目の消費スレッドはキューに項目がないので、Monitor.Waitにヒットし、パルスは再び発生しません(おそらく最後の項目以外のすべてが残るまで)ので、2番目のスレッドが待機します無限に

+1

ゲートを介した最初の消費スレッドがデキューすると、queue.Countは0になり、次にプロデューサがアイテムをエンキューすると、PulseAllが再びヒットします。この場合、2番目のスレッドがなぜ座って無限に待機するのかわかりません。 – Steve

0

追加Thread.Sleep(500); Consumer.comsume

に、私は次のことを持って、

C2 Comsuming ITEM1 C1 Comsuming ITEM2 C2 Comsuming ITEM3 C1 Comsuming ITEM4 C2 Comsuming item5 C1 Comsuming item6 C2 Comsuming item7 C1 Comsuming item8 ..... スリープを追加した後、resuleが不確実ではありません。

+0

どの消費者がどの項目を消費するのかは不明であり、著者の目的と矛盾しないと思う。 – Steve

5

まず、問題を再現できません。ここでは、両方のスレッドがいくつかのアイテムを消費しています。私はあなたのマシンが速いと思うが、gwのような睡眠を加えることでそれを解決することが示唆される。 私が提案するものは、プロデューサを同期しようとしないということです。できるだけ早くアイテムを待ち行列に入れ、コンシューマに同期させて各アイテムを扱う人を見せてもらうことです。 私は、迅速な修正を行い、それが正常に動作しているようだ:

static void Main() 
    { 
     Object lockObj = new object(); 
     Queue<string> queue = new Queue<string>(); 
     Producer p = new Producer(queue); 
     Comsumer c1 = new Comsumer(queue, lockObj, "c1"); 
     Comsumer c2 = new Comsumer(queue, lockObj, "c2"); 

     Thread t1 = new Thread(c1.consume); 
     Thread t2 = new Thread(c2.consume); 
     t1.Start(); 
     t2.Start(); 

     Thread t = new Thread(p.produce); 
     t.Start(); 

     Console.ReadLine(); 
    } 
} 
public class Producer 
{ 
    Queue<string> queue; 
    static int seq; 
    public Producer(Queue<string> queue) 
    { 
     this.queue = queue; 
    } 

    public void produce() 
    { 
     while (seq++ < 1000) //just testinng 15 items 
     { 
      string item = "item" + seq; 
      queue.Enqueue(item); 
      Console.WriteLine("Producing {0}", item);     
     } 
    } 
} 

public class Comsumer 
{ 
    Queue<string> queue; 
    Object lockObject; 
    string name; 
    public Comsumer(Queue<string> queue, Object lockObject, string name) 
    { 
     this.queue = queue; 
     this.lockObject = lockObject; 
     this.name = name; 
    } 

    public void consume() 
    { 
     string item; 
     while (true) 
     { 
      lock (lockObject) 
      { 
       if (queue.Count == 0) 
       { 
        continue; 
       } 
       item = queue.Dequeue(); 
       Console.WriteLine(" {0} Comsuming {1}", name, item); 
      }     
     } 
    } 
} 

あなたはまた、消費者のループを遅くするために睡眠を追加することができます。

+0

いいえ、私は実際に元のコードをテストし、c1からの出力とc2からの出力を得ました。 –

0

私はあなたにコードを実行し、c1を吐き出して、c2の消費とスプレットを実行しました。このリンクをmsdnからチェックしたいだけかもしれません:How to: Synchronize a Producer and a Consumer Thread (C# Programming Guide)

+0

この記事は、実際に提案された実装にバグがあります。リンクされた記事のコメントセクションにスクロールして表示します。 –

0

あなたの目的は、複数のスレッドを「並行して」動作させることです。しかし、あなたのコードは効率が悪いです。 2つの消費スレッドは本質的に連続して動作しています。 2つのコンシューマスレッドが本当に並行して実行できるように、実際の作業コードをロックの外側に配置する必要があります。これにより、複数のコアを持つ場合や、作業の特性に応じて単一のコア・マシン上にある場合でも、ランタイムが向上します。それ以外の場合は、実際には複数のスレッドを使用する必要はありません。なぜなら、いずれもスレッドを順番に実行するためです。

+0

Steveさん、ありがとうございます。 – Robs