2009-09-03 165 views
7

我有一個簡單的單生產者/雙消費者代碼,如下所示,但輸出顯示只有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 2009-09-03 03:10:55

+0

嗨南南,請看看[這個生產者消費者的例子](https://stackoverflow.com/questions/733793/implementing-the-producer-consumer-pattern-in-c-sharp/47179576#47179576),它可能會幫助你。 – 2017-11-08 12:58:50

回答

4

出於測試目的,嘗試加入消費者代碼中的時間延遲。可能會出現這樣的情況:「消費」速度如此之快,以至於一個消費者線程在其他消費者線程有機會之前清空隊列。

(編輯)

正如我懷疑,增加一個

Thread.sleep代碼(500);

在消費者線程內(模擬一些冗長的處理正在進行)導致兩個線程都被使用。

2

當隊列計數等於1時,你的生產者只調用Monitor.PulseAll,這並不是很常見,因爲生產者沒有做任何實質性的事情,這意味着通過門的第一個消耗線程會使隊列中的隊列第一個項目,第二個消耗線程將不會在隊列中看到任何項目,因此點擊Monitor.Wait,並且脈衝不會再發生(可能直到剩下最後一個項目爲止),這樣第二個線程就會處於等待狀態無限地。

+1

當第一個消耗線程通過門出現隊列時,queue.Count變爲0,並且下一次生產者入列一個項時,PulseAll將再次被命中。在這種情況下,我看不出爲什麼第二個線程會無限等待。 – Steve 2010-10-06 22:38:11

0

Added Thread.Sleep(500);在Consumer.comsume

然後我有以下的,

C2 Comsuming物品1 C1 Comsuming ITEM2 C2 Comsuming項目3 C1 Comsuming ITEM4 C2 Comsuming ITEM5 C1 Comsuming ITEM6 C2 Comsuming item7 C1 Comsuming item8 .....加入睡眠後,結果不確定。

+0

哪個消費者消費哪個項目是不確定的,我認爲它不會與作者的目的相沖突。 – Steve 2010-10-06 22:35:55

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獲得了一些輸出。 – 2009-09-03 04:23:14

0

我認爲你的目的是有多個消費線程「並行」工作。但是你的代碼效率很低。這兩個消耗線程本質上是連續工作的。實際的工作代碼應該放在鎖之外,這樣兩個使用者線程才能真正並行運行。如果您擁有多個內核或者甚至在單個核心計算機上,這取決於工作的屬性,這會改善運行時間。否則,實際上沒有必要擁有多個消耗線程,因爲無論如何,它們都會消耗順序運行的線程。

+0

謝謝史蒂夫,你有一個例子嗎? – Robs 2011-05-31 18:46:19