2009-09-03 17 views
7

Ho un semplice codice di un produttore/due consumatori come segue, ma l'output mostra che solo C2 sta consumando. Ci sono errori nel mio codice?C# Producer/Consumer pattern

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

Il risultato è:

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

Puoi descrivere che cosa stai cercando di ottenere esattamente? Il tuo esempio sembra un po 'forzato, quindi non posso determinare dal contesto ciò che devi fare. – jrista

+0

Ciao Southsouth, per favore dai un'occhiata a [questo esempio di produttore-consumatore] (https://stackoverflow.com/questions/733793/implementing-the-producer-consumer-pattern-in-c-sharp/47179576#47179576), potrebbe aiutarti. –

risposta

4

Per scopi di test, aggiungere un ritardo di tempo all'interno del codice del consumo. È possibile che il "consumo" sia così veloce che un thread di consumo svuota la coda prima che l'altro thread del consumatore abbia una possibilità.

(edit)

come sospettavo, l'aggiunta di un

Thread.Sleep (500);

all'interno del thread di consumo (per simulare una lunga elaborazione in corso) risultati in entrambi i thread utilizzati.

2

Il produttore chiama Monitor.PulseAll solo quando il numero di code è uguale a 1, che non sarà molto spesso poiché il produttore non fa nulla di sostanza, questo significa che il primo thread di consumo attraverso il gate arriva a deselezionare il primo elemento, il secondo thread di consumo non vedrà elementi in coda e quindi colpirà Monitor.Avviso, e Pulse non si ripeterà più (probabilmente fino a quando non sarà rimasto tutto tranne l'ultimo) in modo che il secondo thread siederà in quell'attesa infinitamente.

+1

Quando il primo thread di consumo attraverso il gate si arresta, il queue.Count diventa 0 e la volta successiva che il produttore accoda un elemento, PulseAll verrà nuovamente colpito. Non riesco a capire perché il secondo thread siederà e attenderà infinitamente in questo caso. – Steve

0

Aggiunto Thread.Sleep (500); in Consumer.comsume

allora ho il seguente,

c2 comsuming voce1 c1 comsuming item2 c2 comsuming item3 c1 comsuming item4 c2 comsuming item5 c1 comsuming Item6 c2 comsuming item7 c1 comsuming item8 ..... il resule non è incerto dopo l'aggiunta di Sleep.

+0

Quale consumatore consuma quale elemento è incerto e penso che non sarebbe in conflitto con lo scopo dell'autore. – Steve

5

In primo luogo, non riesco a riprodurre il problema, qui entrambi i thread consumano alcuni degli elementi. Immagino che la tua macchina sia più veloce ma aggiungere Sleep come suggerito da gw lo risolverà. Quello che vorrei anche suggerire è che non si tenta di sincronizzare il produttore, voglio dire lasciare che accoda gli elementi il ​​più velocemente possibile e lasciare che i consumatori si sincronizzino per vedere chi gestisce ciascun elemento. Ho fatto una modifica rapida e sembra funzionare bene:

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

È inoltre possibile aggiungere il sonno a rallentare i cicli di consumo.

+0

No, ho effettivamente testato il codice originale e ottenuto alcuni output da c1 e alcuni da c2. –

0

Penso che il tuo scopo sia avere più di un thread di consumo che funzioni "in parallelo". Ma il tuo codice è poco efficiente. I due thread di consumo stanno funzionando in modo sequenziale essenzialmente. Il codice di lavoro effettivo dovrebbe essere messo all'esterno del blocco in modo che i due thread di consumo possano essere eseguiti in parallelo reale. Ciò migliora il runtime se si dispone di più core o anche su una macchina single core a seconda della proprietà del lavoro. Altrimenti, in realtà non c'è motivo di avere più di un thread di consumo perché comunque, tutti consumano thread in sequenza.

+0

Grazie Steve, hai un esempio? – Robs