Search code examples
c#multithreadingreaderwriterlockslim

ReaderWriterLockSlim LockRecursionPolicy.SupportsRecursion DeadLock


I have a Queue of Actions for Database Writing which manged by one dedicated Thread. And I have lots of threads that reads from Database whenever they want.

I am using ReaderWriterLockSlim for READ/WRITE access control.

My question is - Why LockRecursionPolicy.SupportsRecursion is not recommended? MSDN docs says:

The use of recursion is not recommended for new development, because it introduces unnecessary complications and makes your code more prone to deadlocks.

How can be deadlock achieved here? For example, If I am trying call EnterReadLock when WriteReadLock already acquired (and I am under SupportsRecursion policy) I get an exception...


Solution

  • Lock recursion refers to taking the same lock multiple times on the same thread without leaving the original lock(s).

    The main issue with this is that to get in that situation in the first place, you probably have serious issues with who handles the necessary synchronization - your locks may be too granular, or too global. Multi-threading is hard, and making it even harder is utter tomfoolery.

    The second big deal is that locks are tied to threads. However, if you're writing asynchronous code, your code may be jumping between different threads willy-nilly, which may mean that code that appears to be taking a recursive lock isn't - the outer lock ends up being owned by a different thread than the inner lock, and you're forever deadlocked with Thread A waiting for Thread B to finish, while B is waiting for A to release the outer lock.

    You mentioned that ReaderWriterLockSlim throws a lot of recursing exceptions even when recursion is enabled. And yes, this means that using recursive locks is a tiny bit safer than when dealing with e.g. ReaderWriterLock or Monitor. The rules are clearly outlined in MSDN:

    For a ReaderWriterLockSlim that allows recursion, the following can be said about the modes a thread can enter:

    • A thread in read mode can enter read mode recursively, but cannot enter write mode or upgradeable mode. If it tries to do this, a LockRecursionException is thrown. Entering read mode and then entering write mode or upgradeable mode is a pattern with a strong probability of deadlocks, so it is not allowed. As discussed earlier, upgradeable mode is provided for cases where it is necessary to upgrade a lock.
    • A thread in upgradeable mode can enter write mode and/or read mode, and can enter any of the three modes recursively. However, an attempt to enter write mode blocks if there are other threads in read mode.
    • A thread in write mode can enter read mode and/or upgradeable mode, and can enter any of the three modes recursively.
    • A thread that has not entered the lock can enter any mode. This attempt can block for the same reasons as an attempt to enter a non-recursive lock.

    A thread can exit the modes it has entered in any order, as long as it exits each mode exactly as many times as it entered that mode. If a thread tries to exit a mode too many times, or to exit a mode it has not entered, a SynchronizationLockException is thrown.

    They did their best to outright disallow recursion that is pretty much guaranteed to cause deadlocks. However, that doesn't mean that there still aren't deadlocks that go unnoticed (after all, you don't need recursion to cause deadlocks - it just gives you plenty of hard to find opportunities for deadlocks). Not to mention that it's pretty hard to do any consistency guarantees in code that routinely recurses its locks - it may mean that some operations are (semi-)atomic when called from an outer lock, but cease to be when they're invoked directly.

    Multi-threading is hard enough as is. Don't make it even harder just because your object design is broken :) A great introduction to multi-threading (in general and in .NET in particular) is Joe Albahari's "Threading in C#", available on the internet for free (thanks, Joe!). ReaderWriterLockSlim in particular is handled in http://www.albahari.com/threading/part4.aspx#_Reader_Writer_Locks