在C#中,使用条件变量的经典模式如下:
lock (answersQueue)
{
answersQueue.Enqueue(c);
Monitor.Pulse(answersQueue); // condition variable "notify one".
}
和其他一些帖子:
lock (answersQueue)
{
while (answersQueue.Count == 0)
{
// unlock answer queue and sleeps here until notified.
Monitor.Wait(answersQueue);
}
...
}
这是我的代码中的一个例子。 如果我将Pulse放在锁定范围之外,它就不会编译。 但是,这是正确的方法: c.f:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686903(v=vs.85).aspx 和: http://www.installsetupconfig.com/win32programming/threadprocesssynchronizationapis11_7.html (搜索“内部”)
当你还处于关键部分时,确实发出睡眠信号是很愚蠢的。因为睡眠线程不能唤醒(不是立即),因为它也是INSIDE的一个批评部分!
因此,我希望.NET或C#Pulse调用实际上只是标记锁定对象,因此当它超出范围时,它实际上会在此时“脉冲”条件变量。否则,它将具有最优性问题。
那么为什么选择Monitor对象的设计呢?
编辑:
我在本文中找到了答案: http://research.microsoft.com/pubs/64242/implementingcvs.pdf “优化信号和广播”部分和前一节关于NT内核以及如何在信号量之上创建条件变量,这是引入“darned队列”的原因。 现在,这使我成为一名更好的工程师。
答案 0 :(得分:1)
当你还处于关键部分时,确实发出睡眠信号是很愚蠢的。因为睡眠线程无法唤醒
Pulse
不希望让线程运行;它只希望在2个队列之间移动一个线程(等待和准备好)。 “不去做某事”是通过Exit
(或lock
的结尾)释放锁定的一部分。实际上,这不是问题,因为Monitor.Pulse
通常恰好在Wait
或Exit
之前发生。
因此,我希望.NET或C#Pulse调用实际上只是标记锁定对象,因此当它超出范围时,它实际上会在此时“脉冲”条件变量。否则,它将具有最优性问题。
再次;这些是不同的问题:在等待和准备之间移动是一回事;退出锁已经具有实际激活下一个就绪线程的所有代码。
答案 1 :(得分:1)
您不了解同步的基本问题。什么是'监视器',线程睡眠是什么意思,什么意味着它将被唤醒?
监视器是中级同步结构。这不是一个带有总线暂停XCHG操作的低级小型易失性布尔标志,这不是需要许多其他特殊机制的高级线程池处理程序。
在显示器上,很多线程可能会睡眠。那里有逻辑队列,即保持顺序被置于睡眠/唤醒状态,或保证适当的时间安排和公平的机制。我不会详细介绍,所有内容都在网上,甚至在维基上。
除此之外,该操作是PULSE。脉冲是不稳定的。它不会“坚持”。 Pulse会唤醒那些正在睡觉的人。如果在脉冲之后另一个检查显示器,它将进入睡眠状态。
现在想象一下:你有一个5个睡眠线程的队列。一个线程(第6个)现在想要激活它们,而另一个线程(第7个)想要检查监视器。
由于你有四核CPU,所以第6和第7个并行,真正同时运行。那么,告诉我,如果第6个开始脉冲和唤醒并从队列中删除唤醒线程,队列的实现会发生什么,同时第7个开始在那里添加自己?
要解决这个问题,内部队列必须在内部同步并锁定,因此每次只有一个线程会修改它们。
嗯等等。我们偶然发现了一个我们想要SYNCHRONIZE的东西,为了正确地做到这一点,我们需要在另一件事上同步SYNCHRONIZE?不好。因此,在与显示器本身通信之前,实际的LOCK是在外部完成的。这是为了实现单一锁定,而不是引入多层分层锁。
这样,它更简单,更快速,更加资源友好。