我一直在使用此代码作为阻塞Dequeue()
的队列,直到一个元素入队。我已经在几个项目中使用了这段代码几年,所有项目都没有问题......直到现在。我现在正在编写一些代码中的僵局,在调查问题时,我的“怀疑之眼”已经解决了BlockingQueue<T>
。我无法证明这一点,所以我想我会问一些比我聪明的人来检查潜在的问题。你能在这段代码中看到任何可能导致死锁的事吗?
public class BlockingQueue<T>
{
private readonly Queue<T> _queue;
private readonly ManualResetEvent _event;
/// <summary>
/// Constructor
/// </summary>
public BlockingQueue()
{
_queue = new Queue<T>();
_event = new ManualResetEvent(false);
}
/// <summary>
/// Read-only property to get the size of the queue
/// </summary>
public int Size
{
get
{
int count;
lock (_queue)
{
count = _queue.Count;
}
return count;
}
}
/// <summary>
/// Enqueues element on the queue
/// </summary>
/// <param name="element">Element to enqueue</param>
public void Enqueue(T element)
{
lock (_queue)
{
_queue.Enqueue(element);
_event.Set();
}
}
/// <summary>
/// Dequeues an element from the queue
/// </summary>
/// <returns>Dequeued element</returns>
public T Dequeue()
{
T element;
while (true)
{
if (Size == 0)
{
_event.Reset();
_event.WaitOne();
}
lock (_queue)
{
if (_queue.Count == 0) continue;
element = _queue.Dequeue();
break;
}
}
return element;
}
/// <summary>
/// Clears the queue
/// </summary>
public void Clear()
{
lock (_queue)
{
_queue.Clear();
}
}
}
答案 0 :(得分:7)
我认为这可能是你的问题:
Thread 1 Thread 2
Dequeue
Enqueue
if (Size == 0) // Thread 1 gets the lock
lock (_queue) // Thread 2 has to wait
return _queue.Count // Thread 1 sees: Size == 0
_queue.Enqueue // Thread 2 gets the lock
_event.Set
_event.Reset // uh oh
_event.WaitOne // now Dequeue's going to block
// until Enqueue gets called again
// (even though queue isn't empty)
答案 1 :(得分:1)
此代码以多种方式破解。这是一个场景。 if (Size == 0)
和_event.Reset()
之间存在竞争条件。 Enqueue可能在两者之间发射,其信号将丢失。
使用信号量可以更轻松地实现无限长的BlockingQueue。
答案 2 :(得分:0)
我不了解您的要求或您的课程的其他内容,但如果您可以使用.NET 4,您可能需要考虑使用ConcurrentQueue<T>
和BlockingCollection<T>
,它们一起使用,应该给你一个阻塞队列。