2010-04-13 96 views
2

我有一个简单的生产者/消费者场景,其中只有一件产品正在生产/消费。此外,生产者在继续之前等待工作线程完成。我知道那种避免了多线程的整点,但请你只认为它真正需要的是这样的(:Monitor.Wait/Pulse的这种用法是否具有竞争条件?

此代码不能编译,但我希望你的想法:

// 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)包围即可。 我只是好奇,如果它甚至是一个问题开始。

回答

9

无法保证消费者会在另一个生产者之前排队等候或就绪排队。
C#和Java风格的监视器在Wikipedia, under "Implicit condition monitors"上描述。
Monitor会发生什么很好的概述(从this优秀的网站采取的): alt text http://www.albahari.com/threading/waitpulse.png

“可以在C线的等待()允许在A线另一个线程来获得锁和前覆盖M_DATA工作者的线程甚至可以达到它?“

假设SetData()由两个生产者线程调用,P1 & P2
消费者线程C1也启动。
P1,P2C1全部进入就绪队列。
P1先取得锁定。
等待队列为空,Pulse()line B没有影响。
P1等待line C,所以它被放在等待队列中。
就绪队列中的下一个线程获取该锁。
它同样可以是P2C1 - 在第一种情况下,断言失败。
你有一个竞赛条件。

“假设没有发生,并且工作线程处理原始m_data,并最终到达F行,那么当Pulse()熄灭时会发生什么?”

它会将服务员从等待队列移动到就绪队列。
锁由发行Pulse()的线程保存。
通知线程得到一个机会在脉冲线程释放锁定(在就绪队列中可能已经有其他人)后获取锁定。
MSDN, Monitor.Pulse()
“当前拥有指定对象上的锁定的线程调用此方法来发出锁定的下一个线程信号,接收到该脉冲后,等待的线程移至就绪队列。调用Pulse的线程释放锁,就绪队列中的下一个线程(不一定是脉冲线程)获取锁。“

“只有在C线上等待的线程能够获得锁吗?还是会与等待线A的所有其他线程竞争?

就绪队列上的所有线程都“竞争”接下来的锁。
无论他们是直接到达还是等待排队,都可以使用Pulse()

“队列”可能通过其他方式实现。 (不是队列数据结构本身)。
这样一个Monitor的实现可能不能保证公平 - 但可能有更高的整体吞吐量/性能。