2015-06-30 34 views
2

我認爲有一個競賽條件ProducerConsumerQueue
http://www.albahari.com/threading/part2.aspx#_Signaling_with_Event_Wait_Handles。下面是代碼:ProducerConsumerQueue的競爭條件來自線程在C#

using System; 
using System.Threading; 
using System.Collections.Generic; 

class ProducerConsumerQueue : IDisposable 
{ 
    EventWaitHandle _wh = new AutoResetEvent (false); 
    Thread _worker; 
    readonly object _locker = new object(); 
    Queue<string> _tasks = new Queue<string>(); 

    public ProducerConsumerQueue() 
    { 
    _worker = new Thread (Work); 
    _worker.Start(); 
    } 

    public void EnqueueTask (string task) 
    { 
    lock (_locker) _tasks.Enqueue (task); 
    _wh.Set(); 
    } 

    public void Dispose() 
    { 
    EnqueueTask (null);  // Signal the consumer to exit. 
    _worker.Join();   // Wait for the consumer's thread to finish. 
    _wh.Close();   // Release any OS resources. 
    } 

    void Work() 
    { 
    while (true) 
    { 
     string task = null; 
     lock (_locker) 
     if (_tasks.Count > 0) 
     { 
      task = _tasks.Dequeue(); 
      if (task == null) return; 
     } 
     if (task != null) 
     { 
     Console.WriteLine ("Performing task: " + task); 
     Thread.Sleep (1000); // simulate work... 
     } 
     else 
     _wh.WaitOne();   // No more tasks - wait for a signal 
    } 
    } 
} 

考慮以下執行,其中C是消費者線程,P是生產線和T1,T2,T3是執行時間:

T1: C不進入任務執行,因爲隊列爲空

lock (_locker) 
     if (_tasks.Count > 0) 

T2: P調用EnqueueItem(action)

T3: C到達_wh.WaitOne();和永遠等待(假定生產者停止添加新值)

+2

書中的錯誤?廢話!在嚴重的一面,生產者是你的* EnqueueTask調用。我不明白你的評論't1',你參考消費者的代碼的和平,並說生產者。 – Sinatr

+0

「工作」消耗隊列,而任何調用「EnqueueTask」的人都會產生項目。只有當null任務入隊時,worker循環纔會退出,並且通常應該坐在等待Set事件的最後一行。但同意分支數量和推理超過null在消費者循環中看起來相當費力。當然,在生產代碼中,我們會使用像ConcurrentQueue這樣更可信的機制。 – StuartLC

+0

爲什麼代碼會在'_wh.WaitOne()'處「永遠等待」?調用'EnqueueItem(action)'的代碼將在某個時刻調用'_wh.Set()'。 –

回答

2

EnqueueItem每個呼叫做兩件事情 - 它首先保證了在隊列中的至少一個項目(P1) ,然後然後它在AutoResetEvent(P2)上調用Set

消費者在一個循環中執行三項活動 - 它試圖將項目出列(C1),然後處理項目(C2a)或等待AutoResetEvent成爲設置(C2b)。

P1和C1是受鎖定保護的項目,因此我們知道其中一個將在另一個之前發生,並且它們不會交錯。

對於C1得出結論:列表中沒有項目,P1必須出現在列表之後。但是,由於我們知道P2接下來,我們知道AutoResetEvent一定會由P2在某些將來的時間點設定,因此C2b等待將始終得到滿足。

+0

'AutoResetEvent' ..凱克沒有注意到大象... – Sinatr