本文介绍了为什么Monitor.Pulse需要锁定互斥? (。净)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Monitor.Pulse 并PulseAll要求它操作上的锁被锁定在呼叫的时间。这一要求似乎没有必要和有害的性能。我的第一个想法是,这将导致2浪费上下文切换,但是这是由以下nobugz(感谢)纠正。我仍然不能确定它是否涉及到的的潜力的浪费的上下文切换,因为其他线程(县),其中正等待在显示器上已经可用于sheduler,但如果他们被安排,他们将只能够击中互斥,并不必上下文切换之​​前再次运行几条指令。这看起来更简单,更快,如果锁没锁的的调用Monitor.Pulse。

Pthread的条件变量实现了相同的概念,但它没有上述限制:你可以调用调用pthread_cond_broadcast,即使你没有自己的互斥。我认为这是一个证明的要求是没有道理的。

修改:我认识到,一个锁是需要保护的是Monitor.Pulse之前,通常改变了共享资源。我想说,这个锁可能已经被解锁对资源的访问之后但在脉冲之前,考虑到显示器会支持这一点。这将有助于在限制锁其间共享资源被访问的最短时间。因此:

 无效F(项目一)
{
  锁定(somequeue){
    somequeue.add(ⅰ);
  }
  Monitor.Pulse(somequeue); // 错误
}
 

解决方案

究其原因,是因为有记忆障碍,保证线程安全。

共享变量(条件句)被用于确定是否一个脉冲()需要将所涉及的所有主题进行检查。不使用存储屏障,这些变化可能被保存在一个寄存器中,是不可见的,从一个线程到另一个。读取和写入也可以重新排序跨线程观察。

不过,这是从一个锁内访问的变量使用内存屏障,所以他们都可以访问到所有相关的线程。在锁内的所有操作似乎从保持相同的锁其它线程的透视原子执行

此外,多个上下文切换是不需要的,因为你假设。等待的线程被放在一个(名义上FIFO)队列,并且当他们用脉冲()触发,它们不是完全可运行,直到锁被放弃(再次,部分是由于存储器障碍)。

有关的问题的一个很好的讨论,请参见: HTTP:/ /www.albahari.com/threading/part4.aspx#_Wait_and_Pulse

Monitor.Pulse and PulseAll requires that the lock it operates on is locked at the time of call. This requirement seems unnecessary and detrimental for performance. My first idea was that this results in 2 wasted context switches, but this was corrected by nobugz below (thanks). I am still unsure whether it involves a potential for wasted context switches, as the other thread(s) which were waiting on the monitor are already available for the sheduler, but if they are scheduled, they will only be able to run a few instructions before hitting the mutex, and having to context-switch again. This would look much simpler and faster if the lock was unlocked before invoking the Monitor.Pulse.

Pthread condition variables implement the same concept, but it does not have the above described limitation: you can call pthread_cond_broadcast even if you do not own the mutex. I see this as a proof that the requirement is not justified.

Edit:I realize that a lock is required to protect the shared resource that is usually changed before the Monitor.Pulse. I was trying to say that that lock could have been unlocked after access to the resource but before the Pulse, given that Monitor would support this. This would help in limiting the lock to the shortest time during which the shared resource is accessed. As such:

void f(Item i)
{
  lock(somequeue) {
    somequeue.add(i);
  }
  Monitor.Pulse(somequeue);  // error
}
解决方案

The reason has to do with memory barriers and guaranteeing thread safety.

Shared variables (conditionals) that are used to determine whether a Pulse() is needed will be checked by all threads involved. Without a memory barrier, the changes might be kept in a register and be invisible from one thread to another. Reads and writes can also be re-ordered when viewed across threads.

However, variables that are accessed from within a lock use a memory barrier, so they are accessible to all related threads. All operations within the lock appear to execute atomically from the perspective of other threads holding the same lock.

Also, multiple context switches aren't required, as you postulated. Waiting threads are put in a (nominally FIFO) queue, and while they're triggered with Pulse(), they aren't fully runnable until the lock is relinquished (again, in part due to memory barriers).

For a good discussion of the issues, see: http://www.albahari.com/threading/part4.aspx#_Wait_and_Pulse

这篇关于为什么Monitor.Pulse需要锁定互斥? (。净)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 07:41