2015-06-30 72 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