我有一个简单的生产者/消费者场景,其中只有一个项目被生产/消费。此外,生产者在继续之前等待工作线程完成。我意识到这种情况可以避免多线程的全部问题,但请假设它真的需要这样(:
此代码无法编译,但我希望您明白这一点:
// m_data is initially null
// This could be called by any number of producer threads simultaneously
void SetData(object foo)
{
lock(x) // Line A
{
assert(m_data == null);
m_data = foo;
Monitor.Pulse(x) // Line B
while(m_data != null)
Monitor.Wait(x) // Line C
}
}
// This is only ever called by a single worker thread
void UseData()
{
lock(x) // Line D
{
while(m_data == null)
Monitor.Wait(x) // Line E
// here, do something with m_data
m_data = null;
Monitor.Pulse(x) // Line F
}
}
以下是我不确定的情况:
假设许多线程使用不同的输入调用SetData()。 只有其中一个进入锁内,其余部分将被阻挡在A线上。 假设锁定内部的那个设置 m_data 并进入C行。
问题:C行上的Wait()是否允许A行的另一个线程获得锁定并在工作线程到达之前覆盖 m_data ?
假设没有发生,并且工作线程处理原始 m_data ,并最终进入F行,当Pulse()关闭时会发生什么?
只有在C行等待的线程才能获得锁定吗?或者它是否会与在A线上等待的所有其他线程竞争?
基本上,我想知道Pulse()/ Wait()是否特别“在引擎盖下”相互通信,或者它们是否与lock()处于同一级别。
这些问题的解决方案,如果它们存在,当然是显而易见的 - 只需用另一个锁定SetData() - 比如lock(y)。 我只是好奇,如果它开始是一个问题。
答案 0 :(得分:9)
无法保证消费者将在另一个制作人之前排队等候或准备好的队列
C#和Java风格的监视器在Wikipedia, under "Implicit condition monitors"中描述
很好地概述Monitor
中发生的事情(摘自this优秀网站):
alt text http://www.albahari.com/threading/waitpulse.png
“C行上的Wait()是否允许A行的另一个线程获得锁定并在工作线程到达之前覆盖m_data?”
假设SetData()
由两个生产者线程调用, P1 &的 P2 即可。
消费者线程 C1 也会启动
P1 , P2 和 C1 都进入就绪队列。
P1 首先获得锁定
等待队列为空,Pulse()
上的line B
无效
P1 等待line C
,因此将其置于等待队列中
就绪队列中的下一个线程获取锁定
同样可以 P2 或 C1 - 在第一种情况下,断言失败。
你有竞争条件。
“假设没有发生,并且工作线程处理原始m_data,并最终进入F行,当Pulse()关闭时会发生什么?”
它会将服务员从等待队列移动到就绪队列
锁定由发出Pulse()
的线程保持
在脉冲线程释放锁定后,通知的线程有机会获取锁定(就绪队列中可能已有其他人)。
来自MSDN, Monitor.Pulse():
“当前拥有指定对象锁定的线程调用此方法来指示锁定行中的下一个线程。收到脉冲后,等待线程被移动到就绪队列。当调用Pulse的线程时释放锁定,就绪队列中的下一个线程(不一定是脉冲线程)获取锁定。“
“只有在C线上等待的线程能够获得锁定吗?或者它是否会与在A线上等待的所有其他线程竞争?”
就绪队列中的所有线程“竞争”下一次锁定。
如果他们直接或通过Pulse()
从等待队列到达那里并不重要。
“队列”可以通过其他方式实现。 (不是队列数据结构)
这样Monitor
实现可能无法保证公平性 - 但可能具有更高的整体吞吐量/性能。 子>