views:

55

answers:

2

if there are two threads as producer/consumer is it good idea to have following line to prevent deadlocks. I'm aware of live locks but suppose they do a lot of work before calling this Wait() method:

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

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

Here it's impossible both threads be in waiting state.

+2  A: 

This seems overly complicated. Just handle your locking correctly in the first place, and avoid the issue. If you only have two threads, and they are trying to acquire the same, single lock (correctly), you shouldn't have deadlocks. A deadlock means there is something else occurring here.

That being said, if you have the option of using the TPL via .NET 4 (or the Rx Extensions on .NET 3.5), you might want to consider using BlockingCollection<T> instead. It's ideally suited to use in a producer/consumer scenario, and works in a lockless manner.

Reed Copsey
Deadlock occurs when speed of producing or consuming is very high. For example when producer fills the queue and goes to sleep the queue gets empty and both threads remain waiting or if consumer checks and there are nothing for consume just before he goes to sleep producer fills the queue and both go to sleep. I'm not gonna lock the whole produce or consume statements due to performance issues. Blocking collection is not good for single producer/consumer where performance is matter.
Xaqron
@ Xaqron: have you actually measured that introducing a lock would that significantly impact the performance of your application? It seems like premature optimization which leads to a more complicated design.
BrokenGlass
@Xaquon: BlockingCollection<T> is actually the opposite - it's incredibly well optimized for single producer, single consumer. It is likely to be much faster than using locks on a standard collection, even if the locking is minimal.
Reed Copsey
+1  A: 

If your intention is to create a paired variant of the producer-consumer pattern then the sequence is Pulse before Wait for the producer and Wait before Pulse for the consumer. You can reference figure 5 in Joe Duffy's article on this. Howerver, keep in mind that since his implementation performs an unconditional Wait in the Enqueue method a ping-pong like effect will occur between the producer and the consumer. The queue, in his implementation, can only ever have one item per producer. So if that is your intention then this your ticket. Otherwise, you can adapt it as-is and apply some condition1 to the Wait in the Enqueue method to make it behave more like a real FIFO buffer.

However, like Reed, I question why BlockingCollection could not be used. This collection should be very efficient since it uses a lock-free strategy for the Add and Take methods. Of course, like I mentioned above, if you really want the paired variant then this collection will not meet your requirements and you will have to roll your own using Joe Duffy's as a starting point.

1Just remember to use a while loop instead of an if check before applying the wait. Monitor.Wait simply waits for a change in the lock state and nothing more so you have to recheck the wait condition.

Brian Gideon
+1 for producer/consumer wait/pulse order. BlockingCollection is not flexible enough for me, since I pulse producer at different points of consuming. Blocking collection would pulse it as soon as there's a room for another item while I let him rest for my scenario.
Xaqron
@Xaqron: You'll have to roll your own blocking queue then.
Brian Gideon