C#线程中ProducerConsumerQueue中的争用条件

本文关键字:争用条件 ProducerConsumerQueue 线程 | 更新日期: 2023-09-27 18:00:33

我认为来自
ProducerConsumerQueue中存在竞赛条件http://www.albahari.com/threading/part2.aspx#_Signaling_with_Event_Wait_Handles.这是代码:

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();并永远等待(假设生产者停止添加新值)

C#线程中ProducerConsumerQueue中的争用条件

EnqueueItem的每次调用都会做两件事——它首先确保队列中至少有一个项目(P1),然后在AutoResetEvent上调用Set(P2)。

消费者在一个循环中执行三个活动——它试图将一个项目(C1)出列,然后处理该项目(C2a)或等待AutoResetEvent设置(C2b)。

P1和C1是受锁保护的项目,因此我们知道其中一个将先于另一个发生,并且它们不会交错。

对于C1得出列表中没有项目的结论,P1必须出现在它之后。但是,由于我们知道P2紧随其后,我们知道AutoResetEvent肯定会在某个未来时间点由P2设置,因此C2b等待将始终满足。