锁递归是指在同一个线程上多次获取同一个锁而不离开原始锁。
这样做的主要问题是,首先要解决这种情况,您可能对谁处理必要的同步有严重的问题——您的锁可能太细化或太全局化。多线程是困难的,而让它变得更加困难是完全愚蠢的。
第二个大问题是锁与线程相关联。但是,如果您正在编写异步代码,您的代码可能会在不同的线程之间随意跳转,这可能意味着看起来正在使用递归锁的代码不是 - 外部锁最终由不同的线程拥有比内部锁,你永远死锁与线程 A 等待线程 B 完成,而 B 正在等待 A 释放外部锁。
您提到ReaderWriterLockSlim
即使启用递归也会引发很多递归异常。是的,这意味着使用递归锁比处理 egReaderWriterLock
或时更安全一点Monitor
。规则在 MSDN 中有明确的概述:
对于允许递归的 ReaderWriterLockSlim,线程可以进入的模式如下:
- 处于读模式的线程可以递归地进入读模式,但不能进入写模式或可升级模式。如果它尝试这样做,则会抛出 LockRecursionException。进入读模式,然后进入写模式或可升级模式,是一种极有可能发生死锁的模式,因此是不允许的。如前所述,升级模式是为需要升级锁的情况提供的。
- 处于可升级模式的线程可以进入写模式和/或读模式,并且可以递归地进入这三种模式中的任何一种。但是,如果有其他线程处于读取模式,则尝试进入写入模式会阻塞。
- 处于写模式的线程可以进入读模式和/或可升级模式,并且可以递归地进入这三种模式中的任何一种。
- 没有进入锁的线程可以进入任何模式。这种尝试可能会因为与尝试进入非递归锁相同的原因而阻塞。
一个线程可以以任何顺序退出它进入的模式,只要它退出每个模式的次数与它进入该模式的次数完全相同。如果线程尝试退出模式太多次,或者退出它尚未进入的模式,则会引发 SynchronizationLockException。
他们尽最大努力彻底禁止递归,这几乎肯定会导致死锁。然而,这并不意味着仍然没有被忽视的死锁(毕竟,你不需要递归来导致死锁——它只是给你很多很难找到死锁的机会)。更不用说在常规递归其锁的代码中做任何一致性保证是相当困难的——这可能意味着某些操作在从外部锁调用时是(半)原子的,但在直接调用它们时就不再是。
多线程本身就够难了。不要仅仅因为你的对象设计被破坏就让它变得更加困难:) 对多线程(一般来说,特别是在 .NET 中)的一个很好的介绍是 Joe Albahari 的“C# 中的线程”,可在互联网上免费获得(谢谢,乔!)。ReaderWriterLockSlim
特别是在http://www.albahari.com/threading/part4.aspx#_Reader_Writer_Locks中处理