Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lock aqcuired and further attempts to lock do not block: are C# locks re-entrant?

Tags:

I've written a test of what I think should be a valid case for a deadlock. It appears that once the lock has been acquired by an instance of the a class, that instance doesn't need to re-acquire the lock anymore even if I explicitly try to call another method that should lock again.

Here is the class:

internal class Tester
{
    private readonly object _sync = new object();

    public Tester() { }

    public void TestLock()
    {
        lock (_sync)
        {
            for (int i = 0; i < 10; i++)
            {
                Deadlock(i);
            }
        }

    }

    private void Deadlock(int i)
    {
        lock (_sync)
        {
            Trace.WriteLine(i + " no deadlock!");
        }
    }
}

Output:

0 no deadlock!
1 no deadlock!
2 no deadlock!
3 no deadlock!
4 no deadlock!
5 no deadlock!
6 no deadlock!
7 no deadlock!
8 no deadlock!
9 no deadlock!

I would have thought that this would cause a deadlock... can anybody shed some light on this?

like image 436
Kiril Avatar asked Jan 30 '11 22:01

Kiril


People also ask

What does lock () do in C#?

The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. While a lock is held, the thread that holds the lock can again acquire and release the lock. Any other thread is blocked from acquiring the lock and waits until the lock is released.

What does lock () return when a the lock is being held 1?

Returns 1 if the lock is free (no one is using the lock), 0 if the lock is in use, and NULL if an error occurs (such as an incorrect argument).

Is lock acquired?

Locks have two operations: acquire allows a thread to take ownership of a lock. If a thread tries to acquire a lock currently owned by another thread, it blocks until the other thread releases the lock. At that point, it will contend with any other threads that are trying to acquire the lock.

What are locks operating system?

< Operating System Design. Wikipedia has related information at Lock (computer science) Locks are methods of synchronization used to prevent multiple threads from accessing a resource at the same time. Usually, they are advisory locks, meaning that each thread must cooperate in gaining and releasing locks.


2 Answers

Locks in .NET are reentrant. Only acquisitions from other threads are blocked. When the same thread locks the same object multiple times, it simply increments a counter, and decrements it when released. When the counter hits zero, the lock is actually released for access from other threads.

like image 197
Marc Gravell Avatar answered Sep 23 '22 06:09

Marc Gravell


The Monitor, Mutex and ReaderWriterLock classes maintain locks that have thread affinity. The ReaderWriterLockSlim class lets you choose, it has a constructor that takes a LockRecursionPolicy value. Using LockRecursionPolicy.NoRecursion is an optimization, a fairly big one if your locking is really fine-grained.

The Semaphore class is a synchronization class that does not have any thread affinity. This code deadlocks reliably:

class Tester {
    private Semaphore sem = new Semaphore(1, 1);
    public void TestLock() {
        sem.WaitOne();
        for (int i = 0; i < 10; i++) Deadlock(i);
        sem.Release();
    }

    private void Deadlock(int i) {
        if (!sem.WaitOne(100)) Console.WriteLine("deadlock!");
        else {
            sem.Release();
            Console.WriteLine("No deadlock!");
        }
    }
}

In general, the thread affine synchronization classes require two threads and two locks to deadlock. The standard pattern is for one thread to acquire locks A and B, for the other to acquire B and A. The order is important.

There are less obvious deadlocks scenarios around in .NET programming, induced by locks that you cannot see because they are built-in to the .NET framework code. A very classic one is for BackgroundWorker. You could write code in the UI thread that spins on the Busy property, waiting for the BGW to complete. That always deadlocks when the BGW has a RunWorkerCompleted event handler. It cannot run until the UI thread goes idle, the BGW's Busy property won't be false until the event handler finished running.

like image 35
Hans Passant Avatar answered Sep 21 '22 06:09

Hans Passant