【问题标题】:Why is queue lock in this example necessary为什么这个例子中的队列锁是必要的
【发布时间】:2016-05-15 18:42:24
【问题描述】:

我得到了如下所示的示例。我不知道为什么队列 SyncRoot 上存在锁定,而这两种连贯算法都使用同一个对象完全锁定。

据说需要队列锁。

public class CrudeThreadPool
{
    static readonly int MaxWorkThreads = 4;
    static readonly int WaitTimeout = 2000;

    public delegate void WorkDelegate();

    public CrudeThreadPool() {
        stop = false;
        workLock = new Object();
        workQueue = new Queue();
        threads = new Thread[ MaxWorkThreads ];

        for( int i = 0; i < MaxWorkThreads; ++i ) {
            threads[i] =
                new Thread( new ThreadStart(this.ThreadFunc) );
            threads[i].Start();
        }
    }

    private void ThreadFunc() {
        lock( workLock ) {
            do {
                if( !stop ) {
                    WorkDelegate workItem = null;
                    if( Monitor.Wait(workLock, WaitTimeout) ) {
        
                        lock( workQueue.SyncRoot ) {
                            workItem =
                                (WorkDelegate) workQueue.Dequeue();
                        }
                        workItem();
                    }
                }
            } while( !stop );
        }
    }

    public void SubmitWorkItem( WorkDelegate item ) {
        lock( workLock ) {
            lock( workQueue.SyncRoot ) {
                workQueue.Enqueue( item );
            }

            Monitor.Pulse( workLock );
        }
    }

    public void Shutdown() {
        stop = true;
    }

    private Queue         workQueue;
    private Object        workLock;
    private Thread[]      threads;
    private volatile bool stop;
}

锁定队列SyncRoot,即lock(workQueue.SyncRoot )的原因是什么?

【问题讨论】:

  • 你从哪里得到那个代码,你为什么认为它甚至可以工作?
  • 该代码绝对有效。它来自一本教科书。
  • 如果您尝试多次实际运行此代码,您会发现它已损坏。您排队 10 个项目,但随机数量的项目得到处理。有时 10,有时 8,有时 4,有时 0。写它的人不明白他在做什么(尤其是 Monitor.Pulse 和 Monitor.Wait 是如何工作的),所以没有太多理由讨论他为什么锁定队列访问。
  • 这只是一个例子。绝对不需要处理任何特定数量的项目。
  • 此代码已损坏,因为您可能从不处理放入队列的项目。如果你放了 10 件物品——除了它们之外,你至少要在将来的某个时间处理它们。这段代码不是这样。使用损坏的代码,您可以添加或删除队列锁定 - 没关系。

标签: c# multithreading locking syncroot


【解决方案1】:

内部锁实际上并不是必需的,因为只要没有再次达到等待,锁就会被持有并阻塞所有生产者。因此这应该有效:

private void ThreadFunc() {
   do {
        if( !stop ) {
            WorkDelegate workItem = null;
            lock( workLock ) {
                if( Monitor.Wait(workLock, WaitTimeout) ) {
                    workItem = (WorkDelegate) workQueue.Dequeue();
                }
            }
            if (workItem != null) workItem();
        }
    } while( !stop );
}

public void SubmitWorkItem( WorkDelegate item ) 
{
    lock( workLock ) {
        workQueue.Enqueue( item );

        Monitor.Pulse( workLock );
    }
}

Joseph Albahari's site 是线程场景的绝佳参考。 虽然这是一个经典的生产者/消费者场景,但我建议您使用BlockingCollection

【讨论】:

  • 这只会抛出队列为空的异常——仅此而已。
  • 不幸的是,这并没有变得更好 - 现在,当队列为空时,您不断循环循环,无所事事并浪费 CPU 资源:) 在示例代码中 Monitor.Wait 和 Monitor.Pulse 用于发送信号.
  • @Evk 感谢您让我停下来思考这个问题。
  • @Slugart 正如你所说,我的结论是队列锁没有必要
  • 正如@Evk 所说,代码似乎从一开始就很糟糕。如果问题是实现一个生产者消费者队列需要多少个锁对象,那么答案就是一个。
猜你喜欢
  • 1970-01-01
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多