我正在尝试为常规Queue类实现一个线程安全的包装器。但似乎有几个线程可能获得相同的锁。所以我的问题是:
1.它怎么样?
2.如何避免?
任何有用的建议都很高兴
这是我的队列:
public class SafeQueue<T> : SimpleQueue<T>
{
private readonly object sync = new object();
private readonly Queue<T> queue = new Queue<T>();
public override T Dequeue()
{
T item;
lock (sync)
{
Debug.Print("{1:mm ss ffff} {0} locked in dequeue", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
item = queue.Dequeue();
}
Debug.Print("{1:mm ss ffff} {0} unlocked dequeue", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
return item;
}
public override void Enqueue(T item)
{
lock (sync)
{
queue.Enqueue( item );
}
}
public override IEnumerator<T> GetEnumerator()
{
Queue<T> q;
lock (sync)
{
q = new Queue<T>(queue);
}
return ((IEnumerable<T>) q).GetEnumerator();
}
public override int Count
{
get
{
int c;
lock (sync)
{
Debug.Print("{1:mm ss ffff} {0} locked in count", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
c = queue.Count;
}
Debug.Print("{1:mm ss ffff} {0} unlocked count. Ret {2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now,c);
return c;
}
}
}
这是我使用它的部分(工作是一个线程函数):
private readonly SafeQueue<string> taskQueue = new SafeQueue<string>();
private volatile bool stop = false;
...
private void Work()
{
while ( !stop )
{
string dir = null;
if (taskQueue.Count > 0)
{
dir = taskQueue.Dequeue();
}
...
所以,问题基本上是:两个线程获取Count属性的锁并返回一个正值。因为他们都试图将一个元素出列。
答案 0 :(得分:4)
一个明显的错误是,一个项目可能在您检查计数和关联的Dequeue
之间出列。因此,线程安全队列通常具有原子TryDequeue
操作。
通过这样的操作,您的代码变为:
while ( !stop )
{
string dir;
if(taskQueue.TryDequeue(out dir))
{
}
您的调试打印也可能会产生误导。您只能在实际解锁后的某个时间打印“未锁定”行。因此,在实际解锁和调试输出之间,不同的线程可能会进入锁定并打印其“锁定”行,从而导致输出混乱。
.net有两个内置的线程安全队列:ConcurrentQueue<T>
,它是非阻塞的,BlockingCollection<T>
是阻塞的。