这个 BlockingQueue 线程安全吗?
Is this BlockingQueue Thread safe?
我正在尝试自己实现一个最小的线程安全阻塞队列,
我想到的是:
class BlockingQueue<T>
{
private Queue<T> myQueue = new Queue<T>();
private SemaphoreSlim semaPhore = new SemaphoreSlim(0);
public void Enqueue(T t)
{
lock(myQueue)
{
myQueue.Enqueue(t);
semaPhore.Release();
}
}
public T Dequeue()
{
semaPhore.Wait();
lock(myQueue)
{
return myQueue.Dequeue();
}
}
}
我尝试对多个生产者和消费者进行压力测试,同时以随机时间间隔入队/出队,但没有失败。
但是,如果我仔细查看代码,会不会在 "semaPhore.Wait()" 和 "lock(myQueue)" 命令之间发生了什么?
是的,它可能会发生... Thread.Abort()
会打断这个队列。
semaPhore.Wait();
// Here a Thread.Abort() happens
lock(myQueue) ...
然后发生的事情是队列中有一个元素无法恢复,因为 "free" 信号量插槽比队列项少一个。
除此之外(还有 .NET 4.0 专家编写的 BlockingCollection<>
事实),我会说代码是正确的。 semaPhore.Release()
造成隐式屏障(因为它内部使用了一个lock
),所以内存中的写入顺序没有问题(首先Enqueue()
将是really 完成,则 Release()
将完成)。请注意,它会表现不佳,因为每个操作都需要两个 lock
(你的加上 SemaphoreSlim
中的一个)。
使用我现在提出的建议,令人惊讶的是它使用的代码行更少:)
class BlockingQueue<T>
{
private Queue<T> myQueue = new Queue<T>();
public void Enqueue(T t)
{
lock(myQueue)
{
myQueue.Enqueue(t);
Monitor.Pulse(myQueue);
}
}
public T Dequeue()
{
lock(myQueue)
{
Monitor.Wait(myQueue);
return myQueue.Dequeue();
}
}
}
我正在尝试自己实现一个最小的线程安全阻塞队列, 我想到的是:
class BlockingQueue<T>
{
private Queue<T> myQueue = new Queue<T>();
private SemaphoreSlim semaPhore = new SemaphoreSlim(0);
public void Enqueue(T t)
{
lock(myQueue)
{
myQueue.Enqueue(t);
semaPhore.Release();
}
}
public T Dequeue()
{
semaPhore.Wait();
lock(myQueue)
{
return myQueue.Dequeue();
}
}
}
我尝试对多个生产者和消费者进行压力测试,同时以随机时间间隔入队/出队,但没有失败。
但是,如果我仔细查看代码,会不会在 "semaPhore.Wait()" 和 "lock(myQueue)" 命令之间发生了什么?
是的,它可能会发生... Thread.Abort()
会打断这个队列。
semaPhore.Wait();
// Here a Thread.Abort() happens
lock(myQueue) ...
然后发生的事情是队列中有一个元素无法恢复,因为 "free" 信号量插槽比队列项少一个。
除此之外(还有 .NET 4.0 专家编写的 BlockingCollection<>
事实),我会说代码是正确的。 semaPhore.Release()
造成隐式屏障(因为它内部使用了一个lock
),所以内存中的写入顺序没有问题(首先Enqueue()
将是really 完成,则 Release()
将完成)。请注意,它会表现不佳,因为每个操作都需要两个 lock
(你的加上 SemaphoreSlim
中的一个)。
使用我现在提出的建议,令人惊讶的是它使用的代码行更少:)
class BlockingQueue<T>
{
private Queue<T> myQueue = new Queue<T>();
public void Enqueue(T t)
{
lock(myQueue)
{
myQueue.Enqueue(t);
Monitor.Pulse(myQueue);
}
}
public T Dequeue()
{
lock(myQueue)
{
Monitor.Wait(myQueue);
return myQueue.Dequeue();
}
}
}