Monitor.Wait,Condition变量

41K*_*41K 11 c# multithreading synchronization

给出以下代码片段(在学习线程时在某处找到).

 public class BlockingQueue<T>
    {
        private readonly object sync = new object();
        private readonly Queue<T> queue;
        public BlockingQueue()
        {
            queue = new Queue<T>();
        }

        public void Enqueue(T item)
        {
            lock (sync)
            {
                queue.Enqueue(item);
                Monitor.PulseAll(sync);
            }

        }
        public T Dequeue()
        {
            lock (sync)
            {
                while (queue.Count == 0)
                    Monitor.Wait(sync);

                return queue.Dequeue();
            }

        }
    }
Run Code Online (Sandbox Code Playgroud)

我想要了解的是,

为什么会有一个while循环?

   while (queue.Count == 0)
            Monitor.Wait(sync);
Run Code Online (Sandbox Code Playgroud)

这有什么问题,

 if(queue.Count == 0)
       Monitor.Wait(sync);
Run Code Online (Sandbox Code Playgroud)

事实上,当我看到使用while循环时发现的类似代码时,任何人都可以帮助我理解一个在另一个之上的使用.谢谢.

Bri*_*eon 17

你需要了解什么Pulse,PulseAllWait正在做的.在Monitor保持两个队列:等待队列和就绪队列.线程调用时,Wait它会被移入等待队列.当一个线程调用Pulse它时,只有一个线程从等待队列移动到就绪队列.当一个线程调用PulseAll它时,它会将所有线程从等待队列移动到就绪队列.就绪队列中的线程可以随时重新获取锁定,但只有在当前持有者释放它之后才有效.

基于这些知识,很容易理解为什么在使用时必须重新检查队列数PulseAll.这是因为所有出列线程最终都会被唤醒并且想要尝试从队列中提取项目.但是,如果队列中只有一个项目开始,该怎么办?显然,我们必须重新检查队列计数以避免使空队列出列.

那么,如果你使用Pulse而不是PulseAll?那么结论会是什么?简单if检查仍然存在问题.原因是因为来自就绪队列的线程不一定是下一个获取锁的线程.这是因为Monitor不优先考虑Wait呼叫之上的Enter呼叫.

使用时,while循环是一个相当标准的模式Monitor.Wait.这是因为脉冲线程本身没有语义含义.这只是锁定状态发生变化的信号.当线程在阻塞之后唤醒时,Wait它们应该重新检查最初用于阻塞线程的相同条件,以查看线程现在是否可以继续.有时它不能,所以它应该阻止更多.

这里最好的经验法则是,如果对是否使用if支票或while支票有疑问,那么总是选择一个while循环,因为它更安全.事实上,我会把这个放到极端,并建议总是使用一个while循环,因为使用更简单的if检查没有固有的优势,因为if无论如何检查几乎总是错误的选择.类似的规则适用于选择是否使用PulsePulseAll.如果怀疑使用哪一个,那么总是选择PulseAll.