监控器和脉冲等待-意外行为 [英] Monitor.Pulse & Wait - Unexpected Behaviour

查看:106
本文介绍了监控器和脉冲等待-意外行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

http://www.codeproject.com/Articles/28785/线程同步等待和脉冲去神秘化

队列:

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

The ready queue is the collection of threads that are waiting for a particular lock. The Monitor.Wait methods introduce another queue: the waiting queue. This is required as waiting for a Pulse is distinct from waiting to acquire a lock. Like the ready queue, the waiting queue is FIFO.

推荐模式:

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

These queues can lead to unexpected behaviour. When a Pulse occurs, the head of the waiting queue is released and is added to the ready queue. However, if there are other threads in the ready queue, they will acquire the lock before the thread that was released. This is a problem, because the thread that acquires the lock can alter the state that the pulsed thread relies on. The solution is to use a while condition inside the lock statement

* Q =队列.

通过这种方式,我了解到,当我调用Pulse时,它会在结束前做两件事.首先,它将一个线程从等待中的Q移到准备就绪的Q.其次,它让Ready Q中的一个线程(不知道谁是那个线程)获得了锁.不管谁获取锁(来自等待Q的线程或出于某种原因处于就绪Q的线程).

By that, I understand that when I call Pulse, it does 2 things before it ends. Firstly, it removes one thread from the waiting Q to the ready Q. Secondly, it lets one thread (without knowing who is that thread) in the Ready Q acquire the lock; it doesn't care who acquires the lock (the thread that came from the waiting Q or a thread that was in the ready Q for some reason).

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

If I'm right about that then why is putting a while before Monitor.Wait helping to fix the problem (problem - the pulse ends even if the thread that came from the waiting Q didn't acquire the lock)?

A . 告诉我我对Monitor.Pulse的目的是否正确.

A. Tell me if I'm right about the purpose of Monitor.Pulse.

B . 我为什么需要在Monitor.Wait

B. Why do I need to put a while before 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);
        }
    }   
}

推荐答案

假设您要编写生产者/消费者队列-每次生产商品时都要Pulse,而消费者需要等到有商品要消费的物品.您将这样编写代码:

Imagine you're trying to write a producer/consumer queue - you Pulse each time you produce an item, and a consumer needs to wait until there's an item to consume. You'd write code like this:

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

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

Suppose you didn't have the while loop, and instead wrote:

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

现在假设您已经有一个线程在等待,然后在lock语句之前有另一个线程...然后,生产者将监视程序脉冲化(当然,将一个项目添加到队列中).

Now suppose you have one thread already waiting, and then another thread just before the lock statement... then a producer pulses the monitor (and adds an item to the queue, of course).

在那时,甚至还没有到达锁的线程将成为第一个获取锁的线程是完全可行的……在这一点上,等待"线程获得该锁时,队列将再次为空.仅使用一个if语句,没有循环,当队列为空时,您最终将出队,这将失败.

At that point, it's entirely feasible that the thread which hasn't even got to the lock yet will be the first to aquire the lock... at which point by the time the "waiting" thread acquires the lock, the queue would be empty again. With just a single if statement, without looping, you'd end up dequeuing when the queue is empty, which would fail.

在while循环中,您将再次等待直到产生下一个项目,这正是您真正想要的.

With the while loop, you'll wait again until the next item is produced, which is what you really want.

这篇关于监控器和脉冲等待-意外行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆