2010-10-27 45 views
0

如果有两个线程作为生产者/消费者,那么有下面一行来防止死锁是个好主意。我所知道的活锁,但假设他们做了很多工作,调用此wait()方法之前:线程切换和死锁预防问题

// member variable 
object _syncLock = new object(); 

void Wait() 
{ 
    lock (_syncLock) 
     { 
     Monitor.Pulse(_syncLock); 
     Monitor.Wait(_syncLock); 
     } 
} 

这是不可能的两个线程处于等待状态。

+0

通过扩大锁定范围解决了问题,并且移除了Wait()和Pulse()。我从里德那里学到的是,为什么我只用两条线索和已知的生产者/消费者模式来解决这个问题呢?所以,重新设计的时候在看大图。关键是在Visual Studio中寻找代码时,代码行比CPU周期更有意义,因此如果事实上代码行不能比较上下文切换,那么很容易使锁定时间最小化。 – Xaqron 2010-10-28 03:17:16

回答

2

这似乎过于复杂。首先正确处理你的锁定,并避免这个问题。如果你只有两个线程,并且他们试图获得相同的单一锁(正确),你就不应该有死锁。死锁意味着这里还有其他事情发生。这就是说,如果您可以选择通过.NET 4(或.NET 3.5上的Rx Extensions)使用TPL,则可以考虑使用BlockingCollection<T>代替。它非常适合在生产者/消费者场景中使用,并以无锁方式工作。

+0

当生产或消费的速度非常高时发生死锁。例如,当生产者填充队列并进入睡眠状态时,队列变为空,并且两个线程都保持等待,或者如果消费者检查并且在他进入睡眠之前没有什么消耗,那么生产者填充队列并且都进入睡眠状态。由于性能问题,我不会锁定整个产品或消费报表。阻碍收集对于单一的生产者/消费者而言并不是好事,因为性能是重要的。 – Xaqron 2010-10-27 17:13:03

+0

@ Xaqron:您是否真的评估过引入锁会对应用程序的性能产生显着影响?这似乎是不成熟的优化,导致更复杂的设计。 – BrokenGlass 2010-10-27 18:51:12

+0

@Xaquon:BlockingCollection 实际上恰恰相反 - 它对于单一生产者,单一消费者而言是非常优化的。即使锁定很小,它可能比在标准集合上使用锁定要快得多。 – 2010-10-27 19:49:03

1

如果你的目的是建立生产者 - 消费者模式的配对变种则序列是PulsePulse为消费者之前Wait为生产者和Wait之前。您可以参考Joe Duffy's article on this中的图5。 Howerver,请记住,由于他的实施在Enqueue方法中执行无条件Wait,所以生产者和消费者之间会发生乒乓效应。在他的实施中,队列中每个生产者只能有一个项目。所以,如果这是你的意图,那么这是你的票。否则,您可以按照原样对其进行调整,并将中的某些条件应用于Enqueue方法中的Wait,以使其表现得更像真正的FIFO缓冲区。

但是,像里德一样,我怀疑为什么BlockingCollection无法使用。此集合应该非常高效,因为它为AddTake方法使用了无锁策略。当然,就像我上面提到的那样,如果你真的想要配对变体,那么这个集合将不会满足你的要求,你将不得不使用Joe Duffy作为起点。

只需记住将等待前使用while循环而不是if检查。 Monitor.Wait只是等待锁定状态的改变,而不是更多,因此您必须重新检查等待条件。

+0

+1生产者/消费者等待/脉冲订单。 BlockingCollection对我来说不够灵活,因为我在不同的消费点监督生产者。只要有空间存放另一件物品,阻止收藏品就会发出冲击,同时我让他为我的场景休息。 – Xaqron 2010-10-28 03:23:49

+0

@Xaqron:然后你必须推出你自己的阻塞队列。 – 2010-10-28 15:47:27