0

通常の Queue クラスにスレッドセーフなラッパーを実装しようとしています。しかし、複数のスレッドが同じロックを取得する可能性があるようです。私の質問は次のとおり
です。
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();
            }
...

したがって、問題は基本的に次のとおりです。2 つのスレッドが Count プロパティのロックを取得し、正の値を返します。それ以来、両方とも要素をデキューしようとします。

4

1 に答える 1

4

明らかなバグの 1 つは、カウントのチェックと関連するDequeue. このため、スレッドセーフなキューには通常、アトミックTryDequeue操作があります。

このような操作を行うと、コードは次のようになります。

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

デバッグ出力も誤解を招く可能性があります。実際のロック解除が発生してからしばらくしてから、「ロック解除された」行のみを印刷します。したがって、実際のロック解除とデバッグ出力の間に、別のスレッドがロックに入り、「ロックされた」行を出力して、混乱を招く出力になる可能性があります。

ConcurrentQueue<T>.netには 2 つのスレッド セーフ キューが組み込まれていますBlockingCollection<T>

于 2012-07-25T06:16:19.797 に答える