0

BlockingCollection<T>을 사용하여 생산자/소비자 패턴을 구현하려고하므로 간단한 콘솔 응용 프로그램을 작성하여 테스트 해 보았습니다.BlockingCollection.GetConsumingEnumerable을 올바르게 사용하는 방법?

  • 내가 내 Main 메서드에서 대기/차단의 올바른 방법을 알고하지 않습니다

    public class Program 
    { 
        public static void Main(string[] args) 
        { 
         var workQueue = new WorkQueue(); 
         workQueue.StartProducingItems(); 
         workQueue.StartProcessingItems(); 
    
         while (true) 
         { 
    
         } 
        } 
    } 
    
    public class WorkQueue 
    { 
        private BlockingCollection<int> _queue; 
        private static Random _random = new Random(); 
    
        public WorkQueue() 
        { 
         _queue = new BlockingCollection<int>(); 
    
         // Prefill some items. 
         for (int i = 0; i < 100; i++) 
         { 
          //_queue.Add(_random.Next()); 
         } 
        } 
    
        public void StartProducingItems() 
        { 
         Task.Run(() => 
         { 
          _queue.Add(_random.Next()); // Should be adding items to the queue constantly, but instead adds one and then nothing else. 
         }); 
        } 
    
        public void StartProcessingItems() 
        { 
         Task.Run(() => 
         { 
          foreach (var item in _queue.GetConsumingEnumerable()) 
          { 
           Console.WriteLine("Worker 1: " + item); 
          } 
         }); 
    
         Task.Run(() => 
         { 
          foreach (var item in _queue.GetConsumingEnumerable()) 
          { 
           Console.WriteLine("Worker 2: " + item); 
          } 
         }); 
        } 
    } 
    

    그러나 내 디자인 3 문제가 있습니다. 간단한 빈 while 루프를 수행하는 것은 매우 비효율적이며 응용 프로그램이 종료되지 않도록 간단하게 낭비하는 CPU 사용량을 나타냅니다.

  • 내 디자인에 또 다른 문제가 있습니다.이 간단한 응용 프로그램에서 아이템을 무기한 생산하며 절대 멈추지 말아야합니다. 실제 환경에서는 결국 파일을 종료해야합니다 (예 : 처리 할 파일이 부족한 경우). 이 경우 Main 메서드에서 완료 될 때까지 어떻게 기다려야합니까? StartProducingItemsasync으로 작성한 다음 await으로 만드시겠습니까?

  • GetConsumingEnumerable 또는 Add이 예상대로 작동하지 않습니다. 제작자는 끊임없이 항목을 추가해야하지만 한 항목을 추가 한 다음 더 이상 추가하지 않습니다. 이 한 항목은 소비자 중 한 명이 처리합니다. 두 소비자는 추가 될 품목을 기다리는 것을 차단하지만 아무 것도 없습니다. Take 방법을 알고 있지만 while 루프에서 Take을 다시 돌리면 꽤 낭비적이고 비효율적 인 것처럼 보입니다. CompleteAdding 메쏘드가 있습니다 만, 다른 어떤 것도 추가 할 수 없으며, 시도해도 예외가 발생하므로 적합하지 않습니다.

둘 다 소비자가 내가 디버깅하는 동안 스레드 사이를 전환 할 수 있습니다로, 차단하고 새 항목을 기다리고 사실에 있다는 것을 확실히 알고 :

enter image description here

편집 : 나는 '

의견 중 하나에서 변경 사항을 제안했지만 Task.WhenAll은 여전히 ​​즉시 반환됩니다.

public Task StartProcessingItems() 
{ 
    var consumers = new List<Task>(); 

    for (int i = 0; i < 2; i++) 
    { 
     consumers.Add(Task.Run(() => 
     { 
      foreach (var item in _queue.GetConsumingEnumerable()) 
      { 
       Console.WriteLine($"Worker {i}: " + item); 
      } 
     })); 
    } 

    return Task.WhenAll(consumers.ToList()); 
} 
+0

'생산자는 지속적으로 항목을 추가해야하지만 항목을 하나 추가 한 다음 더 이상 추가하지 않습니다. 이 한 항목은 소비자 중 한 명이 처리합니다. 두 소비자는 추가 될 품목을 기다리는 것을 차단하지만 아무도는'그래, 네. 그 밖의 무엇을 기대 했습니까? 소비자가 항목이 추가 될 때까지 차단되므로 차단 컬렉션이라고합니다. –

+0

아니요, 문제는 제작자가 항목을 하나 추가 한 다음 중지한다는 것입니다. – user9993

+0

그것이 코딩 방법입니다. 단일 항목을 추가하는 작업을 시작합니다. 루프가 없습니까? –

답변

4

GetConsumingEnumerable() 차단 중입니다. 당신이 지속적으로 큐에 추가 할 경우, 당신은 루프에서 _queue.Add로 통화를 보류해야한다 : 당신이 눌렀 전에 마무리에서 메인 스레드를 방지하기 위해 Console.ReadLine() 메서드를 호출 할 수있는 Main() 방법에 관한

public void StartProducingItems() 
{ 
    Task.Run(() => 
    { 
     while (true) 
      _queue.Add(_random.Next()); 
    }); 
} 

키 :

+0

Enter를 누르면 마침내 끝납니다. 더 좋은 방법이 있어야합니다. 아마도 StartProducingItems를 만드는 것이 비동기입니까? – user9993

+0

@ user9993'StartProcessingItems'에서 만든 작업을 노출시킨 다음,'Main()'에서'.Wait()'을 호출하십시오. 여러 명의 근로자가있는 경우 'Task.WhenAll'을 사용하여 집계 된 작업을 만들고 대신 노출하십시오 –

+0

WorkQueue 클래스에서 작업을 노출하면 이러한 작업을 기다릴 수 있습니다. 그러나 생산자에게 농산물 생산을 중단하라고 어떻게 말할 것입니까? – mm8