views:

101

answers:

2

Possible Duplicate:
Re-entrant locks in C#

I've looked here on StackOverflow and on MSDN, and can't believe that I couldn't find this question lingering out there on the internets.

Let's say I have a class with a private member that I want to access in several public methods. These public methods will be called by different threads, hence the need for synchronization.

public class MyClass
{
    private Object SomeSharedData = new Object();

    public void MethodA()
    {
        lock( SomeSharedData) {
            // do something
            MethodB();
        }
    }

    public void MethodB()
    {
        lock( SomeSharedData) {
            // do something
        }
    }
}

Note that MethodA and MethodB can be called by users of this class, but MethodA also calls MethodB, which results in a nested locking condition.

Is this guaranteed to be safe? In other words, does .NET handle this by reference counting the lock, so that as I pop out of these methods, the lock gets decremented? Or is .NET performing some magic behind the scenes, whereby it simply ignores all subsequent locks on the object originating from the same thread?

+6  A: 

Yes, locks based on Monitor in .NET are recursive, and counted.

From the docs for Monitor.Enter:

It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.

Whether this is a good thing or not is up for debate.

Jon Skeet
The "up for debate" link goes to the Monitor.Enter documentation. Can you edit your answer and provide the link you intended?
Jim Mischel
@Jon ok, that makes sense. Now I should just test this with a little app, but I assume that if an exception is thrown and not handled within the lock() {} statement, that the ref count is still decremented?
Dave
@Dave it is decremented.
eglasius
@Jim: Oops, no. Will edit.
Jon Skeet
@Dave: Yes, there's effectively a call to Monitor.Exit in a finally block.
Jon Skeet
+2  A: 

Yes, Monitor support recursion, but you should be aware because this behavior differs from one synchronization primitive to another.

For example, ReaderWriterLockSlim by default doesn't support recursion and this code snippet throws exception:

public class MyClass
{
    ReaderWriterLockSlim rw = new ReaderWriterLockSlim();
    //You should explicitly stated that you want to use recursion
    ReaderWriterLockSlim rwWithRecursion = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);

    public void MethodA()
    {
        try {
           rw.EnterReadLock();
           // do something
           MethodB();
        }
        finally {
          rw.ExitReadLock();
        }
    }

    public void MethodB()
    {
        try {
           rw.EnterReadLock(); //throws LockRecursionException
        }
        finally {
          rw.ExitReadLock();
        }
    }
}
Sergey Teplyakov
@Sergey thanks, I'll keep that in mind. I haven't found a need for ReaderWriterLockSlim yet, but I have read about it before and it might come in handy some day, so hopefully I'll remember your advice. :)
Dave