2009-09-03 7 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

+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 만 호출합니다. 이는 생산자가 실제로 아무 것도하지 않기 때문에 발생합니다. 즉, 게이트를 통해 소비 한 첫 번째 스레드가 대기열에서 대기열을 비 웁니다. 첫 번째 항목에서 두 번째 스레드는 대기열에 항목이 없으므로 Monitor.Wait에 도달하고 두 번째 스레드가 대기 상태가되도록 펄스가 다시 발생하지 않습니다 (마지막 항목을 제외한 나머지 항목이 남아있을 때까지). 무한히.

+1

게이트를 통해 첫 번째 소비 스레드가 대기열에서 제외되면 queue.Count는 0이되고 다음에 제작자가 항목을 대기열에 추가하면 PulseAll이 다시 히트됩니다. 이 경우 두 번째 스레드가 앉아서 무한정 기다리는 이유를 알 수 없습니다. – Steve

0

Added Thread.Sleep (500); I는 다음과 같은 한 후 Consumer.comsume

,

C2 Comsuming 항목 1 C1 Comsuming 항목 2 C2 Comsuming 항목 3 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

"평행하게"작동하는 하나 이상의 소비 스레드를 사용하는 것이 귀하의 목적이라고 생각합니다. 하지만 코드가 효율적이지 않습니다. 두 소비 스레드는 본질적으로 순차적으로 작동합니다. 실제 작업 코드는 잠금 장치 외부에 두어 두 개의 소비자 스레드가 실제 병렬로 실행될 수 있도록해야합니다. 작업의 속성에 따라 여러 코어가 있거나 단일 핵심 시스템에있는 경우에도 런타임이 향상됩니다. 그렇지 않으면 실제로는 하나 이상의 스레드를 소비해야 할 필요가 없습니다. 왜냐하면 모든 스레드가 순차적으로 실행되기 때문입니다.

+0

스티브, 고마워요? – Robs