2016-09-22 45 views
1

我正在嘗試使用MonitorC#中實施生產者/消費者類。 想法是,消費者必須阻止,直到生產者有消費者的一些項目,但生產者應該繼續生產。 我的製作人生產一些物品,然後在再次生產之前等待/睡眠一段時間。爲什麼我的製片人有時會永遠封鎖?

我看到生產者從未從Thread.Sleep(time)醒來的問題。 也許某處存在死鎖情況。

請幫我理解這一點。

就像一張紙條,我不希望使用BlockingCollection ... 這裏是我的代碼...

public class ProducerConsumerEx 
    { 
     private object _objLocker = new object(); 
     private Thread _tProducer; 
     private Queue<string> _producerQueue; 
     private bool _keepProducing; 
     public ProducerConsumerEx() 
     { 
      _keepProducing = false; 
      _producerQueue = new Queue<string>(); 
      _tProducer = new Thread(Produce); 
      _tProducer.IsBackground = true; 
     } 

     private void Produce() 
     { 
      while (_keepProducing) 
      { 
       Console.WriteLine($"PRODUCER {Thread.CurrentThread.ManagedThreadId} LOOP"); 
       lock (_objLocker) 
       { 
        string item = DateTime.Now.ToString("HH:mm:ss"); 
        _producerQueue.Enqueue(item); 
        Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} Inserted {item}"); 
        Monitor.Pulse(_objLocker); 
        Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} AF Pulse {item}"); 
       } 
       Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Sleep"); 
       Thread.Sleep(10000); 
       Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} AF Sleep"); 
      } 
     } 
     public void Start() 
     { 
      if (!_keepProducing) 
      { 
       _tProducer.Start(); 
       _keepProducing = true; 
      } 
     } 
     public string Consume() 
     { 
      string val = default(string); 
      Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Consume"); 

      lock (_objLocker) 
      { 
       Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Consume Inside"); 
       if (_producerQueue.Count > 0) 
       { 
        val = _producerQueue.Dequeue(); 
       } 
       else 
       { 
        Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} WAITING"); 
        Monitor.Wait(_objLocker); 
        // 
        if (_producerQueue.Count > 0) 
        { 
         val = _producerQueue.Dequeue(); 
        } 
       } 
      } 
      return val; 
     } 
    } 

而且使用這個類是這樣

static void Main(string[] args) 
     {   
      ProducerConsumerEx pc = new ProducerConsumerEx(); 
      pc.Start(); 
      while (true) 
      { 
       string t = pc.Consume(); 
       Console.WriteLine($"Main {t}"); 
      }  
     } 
+3

你應該切換線'_tProducer.Start();'和'_keepProducing = TRUE;',你永遠不知道如果線程在'_keepProducing = true;'之前或之後啓動。 –

+0

確定'Thread.Sleep'沒有執行後'Console.WriteLine'? – slawekwin

+0

@slawekwin是的,我相信,Console.WriteLine沒有執行,這是我不明白... –

回答

1

要實現生產者/消費者模式,我使用推薦BlockingCollection

一個小例子:

private BlockingCollection<string> _producerQueue; 

    void Consume() 
    { 
     foreach (var item in _producerQueue.GetConsumingEnumerable()) 
     { 
      //do some work there 
     } 
    } 

    void Produce() 
    { 
     _producerQueue.Add("a string to consume"); 
    } 

    public void Start() 
    { 
     Task.Factory.StartNew(Consume); 
    } 

從MSDN:

https://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx

提供了阻斷和邊界爲實現IProducerConsumerCollection線程安全 收藏功能。

你可以找到一些例子有:

http://dotnetpattern.com/csharp-blockingcollection

-1

Monitor.Wait(_objLocker);lock (_objLocker)語句中。這樣生產者永遠不能添加和發信號給消費者。你應該把外面的lock (_objLocker)

有點像Monitor.Wait(_objLocker);(僞)

public string Consume() 
{ 
    string val = default(string); 
    Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Consume"); 

    int count = 0; 

    lock (_objLocker) 
     count = _producerQueue.Count; 

    if (count == 0) 
    { 
     Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} WAITING"); 
     Monitor.Wait(_objLocker); 
    } 

    lock (_objLocker) 
     val = _producerQueue.Dequeue(); 

    return val; 
} 
+0

這就是我想的但它不是[Monitor.Wait/Pulse](https://msdn.microsoft.com/en-us/library/ateab679(v = vs.110).aspx)的工作原理 – slawekwin

+0

從未使用過Monitor。我總是使用resetevents。我可能會認爲顯示器等待/脈衝的工作原理是錯誤的。 –

相關問題