0

我正在尝试为常规 Queue 类实现线程安全的包装器。但似乎多个线程可能会获得相同的锁。所以我的问题是:
1. 怎么可能?
2、如何避免?
3.任何有用的建议都很高兴

这是我的队列:

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 属性的锁并返回一个正值。从那以后,他们都尝试使元素出队。

4

1 回答 1

4

一个明显的错误是,在您检查计数和关联的Dequeue. 出于这个原因,线程安全队列通常具有原子TryDequeue操作。

通过这样的操作,您的代码变为:

while ( !stop )
{
    string dir;
    if(taskQueue.TryDequeue(out dir))
    {
    }

您的调试打印也可能具有误导性。您仅在实际解锁发生后的某个时间打印“解锁”行。因此,在实际解锁和调试输出之间,不同的线程可能会进入锁并打印其“锁定”行,从而导致输出混乱。

.net 有两个内置的线程安全队列:ConcurrentQueue<T>一个是非阻塞的,BlockingCollection<T>一个是阻塞的。

于 2012-07-25T06:16:19.797 回答