这样做并不是很有用,而是被允许这样做很有用。考虑一下您可能经常拥有调用其他公共方法的公共方法。如果公共方法调用了锁,并且调用它的公共方法需要锁定它所做的更广泛的范围,那么能够使用递归锁意味着你可以这样做。
在某些情况下,您可能想使用两个锁定对象,但您将同时使用它们,因此如果您犯了错误,就会有很大的死锁风险。如果您可以处理赋予锁的更广泛的范围,那么在这两种情况下使用相同的对象 - 并在使用这两种对象的情况下递归 - 将消除那些特定的死锁。
但是...
这种有用性值得商榷。
在第一种情况下,我将引用Joe Duffy:
递归通常表示同步设计中的过度简化,这通常会导致代码可靠性降低。一些设计使用锁递归来避免将函数拆分为那些需要锁的函数和那些假设锁已经被占用的函数。诚然,这可以减少代码大小,从而缩短编写时间,但最终会导致设计更加脆弱。
将代码分解到采用非递归锁的公共入口点中总是一个更好的主意,并且持有断言锁的内部工作函数。递归锁调用是导致原始性能开销的冗余工作。但更糟糕的是,依赖递归会使理解程序的同步行为变得更加困难,特别是在不变量应该保持的边界上。通常我们想说,获取锁后的第一行代表一个对象不变的“安全点”,但是一旦引入递归,就不能再自信地做出这个陈述。这反过来又使得在动态组合时确保正确和可靠的行为变得更加困难。
(Joe 在他的博客和his book on concurrent programming 的其他地方有更多关于这个话题的内容。
第二种情况与递归锁条目只会导致不同类型的死锁发生的情况相平衡,或者将争用率推高到可能出现死锁的情况(This guy 说他更喜欢它只是在您第一次递归时遇到死锁,我不同意 - 我更希望它只是抛出一个大异常,使我的应用程序因良好的堆栈跟踪而崩溃)。
最糟糕的事情之一是它在错误的时间进行了简化:当您编写代码时,使用锁递归可以更简单,而不是更多地拆分事物并更深入地思考何时应该锁定什么。但是,当您调试代码时,离开锁并不意味着离开该锁这一事实会使事情复杂化。多么糟糕的方法 - 当我们认为我们知道我们在做什么时,复杂的代码是一种诱惑,可以在你的下班时间享受,所以你不要在上班时沉迷,当我们意识到我们搞砸了我们最希望事情变得美好而简单。
You really don't want to mix them with condition variables.
嘿,POSIX 线程 only has them because of a dare!
至少lock 关键字意味着我们避免了每个Monitor.Enter()s 都没有匹配的Monitor.Exit()s 的可能性,这使得一些风险不太可能发生。直到您需要在该模型之外做一些事情。
使用更新的锁定类,.NET 可以帮助人们避免使用锁递归,而不会阻止那些使用旧编码模式的人。 ReaderWriterLockSlim 有一个构造函数重载,可让您使用它递归,但默认为 LockRecursionPolicy.NoRecursion。
通常在处理并发问题时,我们必须在一个更令人担忧的技术之间做出决定,这种技术可能会给我们带来更好的并发性,但需要更加小心以确保正确性,而更简单的技术可能会带来更差的并发性,但是更容易确定正确性的地方。递归地使用锁为我们提供了一种技术,我们将持有锁的时间更长,并发性较差,而且对正确性的把握也较小,调试也更困难。