Monitor.Pulse&等待 - 意外行为

时间:2012-08-25 10:24:29

标签: c# locking monitor

  

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified

     

队列:

     

就绪队列是等待a的线程的集合   特别锁定。 Monitor.Wait方法引入了另一个队列:   等待队列。这是必需的,因为等待脉冲是不同的   从等待获得锁定。就像准备好的队列一样,等待着   队列是FIFO。

     

推荐模式:

     

这些队列可能会导致意外行为。当脉冲发生时,   等待队列的头部被释放并添加到准备好的队列中   队列。但是,如果就绪队列中还有其他线程,则为它们   将在释放的线程之前获取锁。这是一个   问题,因为获取锁的线程可以改变状态   脉冲线依赖于。解决方案是使用一段时间   锁定语句中的条件

* Q =队列。

据我所知,当我打电话给Pulse时,它会在结束之前做两件事。首先,它将等待Q中的一个线程移除到就绪Q.其次,它允许Ready Q中的一个线程(不知道谁是该线程)获得锁定;它并不关心谁获得锁(来自等待Q的线程或由于某种原因在准备Q中的线程)。

如果我对此表示正确,那么为什么会在while之前Monitor.Wait帮助解决问题(问题 - 即使线程结束,脉冲也会结束来自等待的Q没有拿到锁?)

A 即可。 告诉我,我对Monitor.Pulse的目的是否正确。

即可。 为什么我需要在while

之前加Monitor.Wait

以下答案的完整代码:

class Program
{
    static Queue<int> queue = new Queue<int>();
    static object someMonitor = new object();

    static void Main(string[] args)
    {
        Thread Thread1 = new Thread(WorkAlltheTime);
        Thread1.Name = "Thread1";
        Thread Thread2 = new Thread(WorkAlltheTime);
        Thread2.Name = "Thread2";
        Thread Thread3 = new Thread(WorkOnce);
        Thread3.Name = "Thread3";
        Thread1.Start();
        Thread2.Start();
        Thread.Sleep(1000);
        Thread3.Start();
        Console.ReadLine();
    }

    static void WorkAlltheTime()
    {
        Console.WriteLine("Came in to Ready Q: " + Thread.CurrentThread.Name);
        lock (someMonitor)
        {
            Console.WriteLine("Came out from Ready Q: " + Thread.CurrentThread.Name);
            // Broken!
            while (queue.Count == 0)
            {
                Console.WriteLine("Came in to Waiting Q: " + Thread.CurrentThread.Name);
                Monitor.Wait(someMonitor);
                Console.WriteLine("Came out from Waiting Q: " + Thread.CurrentThread.Name);
            }
            queue.Dequeue();
            Console.WriteLine("Thread: "+Thread.CurrentThread.Name+" Pulled Out");
        }
    }

    static void WorkOnce()
    {
        lock (someMonitor)
        {
            queue.Enqueue(1);
            Monitor.Pulse(someMonitor);
        }
    }   
}

1 个答案:

答案 0 :(得分:9)

想象一下,你正在尝试编写一个生产者/消费者队列 - 每次生成一个项目时你Pulse,而消费者需要等到有一个项目要消耗。你会写这样的代码:

Foo item;
lock(someMonitor)
{
    while (queue.Count == 0)
    {
        Monitor.Wait(someMonitor);
    }
    item = queue.Dequeue();
}
// Use the item

假设您没有拥有while循环,而是写道:

Foo item;
lock(someMonitor)
{
    // Broken!
    if (queue.Count == 0)
    {
        Monitor.Wait(someMonitor);
    }
    item = queue.Dequeue();
}
// Use the item

现在假设你有一个线程已经在等待,然后是另一个线程就在lock语句之前...然后一个生产者用脉冲监视器(当然还有一个项目添加到队列中)。

此时,完全可行的是,尚未获得锁定的线程将是第一个获取锁定的线程......此时“等待”线程获取锁定时,队列将再次为空。只有一个if语句,没有循环,当队列为空时,你最终会出列队列,这会失败。

使用while循环,您将再次等待,直到生成下一个项目,这是您真正想要的。