2009-09-03 13 views
7

Ich habe einfachen One-Producer/Zwei-Verbraucher-Code wie folgt, aber die Ausgabe zeigt, dass nur C2 verbraucht wird. Gibt es Fehler in meinem Code?C# Producer/Consumer Muster

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

Die Ausgabe lautet:

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

Können Sie beschreiben, was genau Sie erreichen möchten? Ihr Beispiel sieht etwas kompliziert aus, sodass ich nicht aus dem Kontext bestimmen kann, was Sie tun müssen. – jrista

+0

Hi Southsouth, bitte sehen Sie sich dieses [Producer-Consumer-Beispiel] an (https://stackoverflow.com/questions/733793/implementing-the-producer-consumer-pattern-in-c-sharp/47179576#47179576), Es kann dir helfen. –

Antwort

4

Zu Testzwecken versuchen, eine Zeitverzögerung in der Verbraucher-Code hinzufügen. Es kann der Fall sein, dass "Verbrauch" so schnell ist, dass ein Verbraucher-Thread die Warteschlange leert, bevor der andere Verbraucher-Thread eine Chance hat.

(Bearbeiten)

Wie ich vermutet wird, das Hinzufügen eines

Thread.Sleep (500);

innerhalb des Consumer-Threads (um eine langwierige Verarbeitung zu simulieren), werden beide Threads verwendet.

2

Ihr Producer ruft Monitor.PulseAll nur dann auf, wenn die Anzahl der Warteschlangen gleich 1 ist, was nicht sehr oft der Fall ist, da vom Produzenten nichts Substanz gemacht wird, bedeutet dies, dass der erste Thread durch das Gate entnimmt erstes Element, das zweite konsumiert Thread wird keine Elemente in der Warteschlange sehen und so das Monitor.Wait treffen, und das Pulse wird nicht wieder passieren (wahrscheinlich bis alle außer dem letzten Element übrig ist), so dass der zweite Thread in dieser Wartezeit sitzen wird unendlich.

+1

Wenn der erste Thread, der durch das Gatter läuft, aus der Warteschlange entfernt wird, wird queue.Count zu 0 und das nächste Mal, wenn der Producer ein Element in die Warteschlange einreiht, wird der PulseAll erneut getroffen. Ich kann nicht sehen, warum der zweite Thread in diesem Fall sitzen und unendlich warten wird. – Steve

0

Hinzugefügt Thread.Sleep (500); in Consumer.comsume

dann folgendes ich habe,

c2 comsuming item1 c1 comsuming Element2 c2 comsuming item3 c1 comsuming item4 c2 comsuming item5 c1 comsuming Item6 c2 comsuming item7 c1 comsuming item8 ..... Das Resule ist nicht nach dem Hinzufügen von Schlaf unsicher.

+0

Welcher Verbraucher konsumiert, welcher Gegenstand unsicher ist, und ich denke, dass es dem Zweck des Autors nicht widersprechen würde. – Steve

5

Erstens kann ich Ihr Problem nicht reproduzieren, hier verbrauchen beide Threads einige der Elemente. Ich schätze, deine Maschine ist schneller, aber das Hinzufügen von Sleep wie gw suggeriert das. Was ich auch vorschlagen würde, ist, dass Sie nicht versuchen, den Produzenten zu synchronisieren, ich meine, lassen Sie es Gegenstände so schnell wie möglich einreihen und lassen Sie die Verbraucher synchronisieren, um zu sehen, wer mit jedem Gegenstand umgeht. machte ich eine schnelle Änderung und es scheint gut zu funktionieren:

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

Sie können auch den Schlaf fügen die Verbraucherkreise zu verlangsamen.

+0

Nein, ich habe den Originalcode getestet und einige Ausgaben von c1 und einige von c2 bekommen. –

0

Ich denke, Ihr Zweck ist es, mehr als ein konsumieren Threads arbeiten "parallel". Aber Ihr Code ist wenig effizient. Die zwei konsumierenden Threads arbeiten sequentiell im Wesentlichen. Der eigentliche Arbeitscode sollte außerhalb der Sperre platziert werden, damit die beiden Consumer-Threads parallel ausgeführt werden können. Dies verbessert die Laufzeit, wenn Sie mehrere Kerne oder sogar einen einzelnen Kernrechner haben, abhängig von der Eigenschaft der Arbeit. Ansonsten ist es eigentlich nicht sinnvoll, mehr als einen Thread zu verwenden, da sowieso alle Threads sequenziell ausgeführt werden.

+0

Danke Steve, hast du ein Beispiel? – Robs