views:

55

answers:

2

Is there an equivalent of Monitor.Pulse and Monitor.Wait that I can use in conjunction with a ReaderWriterLockSlim?

I have a class where I've encapsulated multi-threaded access to an underlying queue. To enqueue something, I acquire a lock that protects the underlying queue (and a couple of other objects) then add the item and Monitor.Pulse the locked object to signal that something was added to the queue.

public void Enqueue(ITask task)
{
    lock (mutex)
    {
        underlying.Enqueue(task);
        Monitor.Pulse(mutex);
    }
}

On the other end of the queue, I have a single background thread that continuously processes messages as they arrive on the queue. It uses Monitor.Wait when there are no items in the queue, to avoid unnecessary polling. (I consider this to be good design, but any flames (within reason) are welcome if they help me learn otherwise.)

private void DequeueForProcessing(object state)
{
    while (true)
    {
        ITask task;
        lock (mutex)
        {
            while (underlying.Count == 0)
            {
                Monitor.Wait(mutex);
            }
            task = underlying.Dequeue();
        }
        Process(task);
    }
}

As more operations are added to this class (requiring read-only access to the lock protected underlying), someone suggested using ReaderWriterLockSlim. I've never used the class before, and assuming it can offer some performance benefit, I'm not against it, but only if I can keep the Pulse/Wait design.

+1  A: 

No, basically. It is optimised to provide some specific/common scenarios. If you go outside those scenarios it won't work. If you need Pulse etc, use Monitor instead.

Marc Gravell
Are there any other built-in .NET mechanisms that can put the current thread back into the waiting queue for a lock, or is that what a monitor <i>does</i>?
Jono
@Jono - `Monitor.Wait`
Marc Gravell
A: 

After playing around a bit (and after accepting Mark's challenge (I agree, my change isn't exactly basic)) I came up with this combination:

ReaderWriterLockSlim rwls;
AutoResetEvent are;
public void Enqueue(ITask task) {
    rwls.EnterWriteLock();
    try {
        underlying.Enqueue(task);
        if (underlying.Count == 1) {
          are.Set();
        }
    } finally {
        rwls.ExitWriteLock();
    }
}
private void DequeueForProcessing(object state) {
    while (true) {
        ITask task;
        rwls.EnterWriteLock();
        try {
            while (underlying.Count == 0) {
                rwls.ExitWriteLock();
                are.WaitOne();
                rwls.EnterWriteLock();
            }
            task = underlying.Dequeue();
        } finally {
            rwls.ExitWriteLock();
        }
        Process(task);
    }
}

To my untrained eye, it seems to fit the bill (though it's a little uglier, to be sure,) and it's not too different from the original version.

Jono