【问题标题】:Monitor.Pulse & Wait - Unexpected BehaviourMonitor.Pulse & Wait - 意外行为
【发布时间】:2012-08-20 17:04:38
【问题描述】:

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

队列:

就绪队列是等待一个线程的集合 特定的锁。 Monitor.Wait 方法引入了另一个队列: 等待队列。这是必需的,因为等待脉冲是不同的 从等待获取锁。就像就绪队列一样,等待 队列是先进先出的。

推荐模式:

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

*Q = 队列。

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

如果我是对的,那么 为什么Monitor.Wait 之前放置一个 while 有助于解决问题(问题 - 即使线程来自等待 Q没有获得锁)?

一个告诉我Monitor.Pulse 的用途是否正确。

B为什么我需要在Monitor.Wait之前加一个while

答案的完整代码如下:

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);
        }
    }   
}

【问题讨论】:

    标签: c# locking monitor


    【解决方案1】:

    假设您正在尝试编写一个生产者/消费者队列 - 您 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 循环,您将再次等待,直到生成下一个项目,这是您真正想要的。

    【讨论】:

    • 谢谢,我编写了您的示例的完整代码,并根据您的解释,我测试了 90 次,现在我完全理解了。谢谢阿吉安!
    猜你喜欢
    • 2016-04-19
    • 1970-01-01
    • 2017-03-10
    • 2019-05-01
    • 2011-07-15
    • 2011-04-27
    • 2019-12-07
    • 2021-12-18
    • 1970-01-01
    相关资源
    最近更新 更多