ReaderWriterLockSlim LockRecursionPolicy.SupportsRecursion DeadLock

Pav*_*rov 3 c# multithreading readerwriterlockslim

我有一个数据库写入操作队列,由一个专用线程管理。我有很多线程可以随时从数据库读取数据。

我使用 ReaderWriterLockSlim 进行读/写访问控制。

我的问题是 - 为什么不推荐 LockRecursionPolicy.SupportsRecursion?MSDN 文档说:

不建议在新开发中使用递归,因为它会引入不必要的复杂性并使您的代码更容易出现死锁。

这里怎么会出现死锁呢?例如,如果我在已获取 WriteReadLock 时尝试调用 EnterReadLock(并且我处于 SupportsRecursion 策略下),则会出现异常...

Lua*_*aan 6

锁递归是指在同一个线程上多次获取同一锁,而不离开原始锁。

这样做的主要问题是,要首先陷入这种情况,您可能会在谁处理必要的同步方面存在严重问题 - 您的锁可能太细粒度或太全局。多线程很难,让它变得更难简直是愚蠢之极。

第二个大问题是锁与线程绑定在一起。但是,如果您正在编写异步代码,您的代码可能会在不同线程之间随意跳转,这可能意味着看似采用递归锁的代码实际上并非如此 - 外部锁最终由不同的线程拥有比内部锁,你永远陷入死锁,线程 A 等待线程 B 完成,而 B 正在等待 A 释放外部锁。

您提到ReaderWriterLockSlim即使启用了递归,也会引发大量递归异常。ReaderWriterLock是的,这意味着使用递归锁比处理 eg或时更安全一些Monitor。MSDN 中明确概述了这些规则:

对于允许递归的 ReaderWriterLockSlim,线程可以进入的模式如下:

  • 处于读模式的线程可以递归地进入读模式,但不能进入写模式或可升级模式。如果尝试这样做,则会抛出 LockRecursionException。进入读模式然后进入写模式或可升级模式是一种死锁概率很大的模式,因此是不允许的。如前所述,可升级模式是为需要升级锁的情况提供的。
  • 可升级模式下的线程可以进入写模式和/或读模式,并且可以递归地进入这三种模式中的任何一种。但是,如果有其他线程处于读取模式,则尝试进入写入模式会被阻止。
  • 处于写模式的线程可以进入读模式和/或可升级模式,并且可以递归地进入这三种模式中的任何一种。
  • 没有进入锁的线程可以进入任何模式。由于与尝试进入非递归锁相同的原因,此尝试可能会被阻止。

线程可以以任何顺序退出它所进入的模式,只要它退出每种模式的次数与进入该模式的次数完全相同。如果线程多次尝试退出某个模式,或者退出尚未进入的模式,则会引发 SynchronizationLockException。

他们尽最大努力彻底禁止递归,这几乎肯定会导致死锁。然而,这并不意味着仍然存在未被注意到的死锁(毕竟,您不需要递归来导致死锁 - 它只是给您提供了很多难以找到死锁的机会)。更不用说在定期递归其锁的代码中很难做到任何一致性保证——这可能意味着某些操作在从外部锁调用时是(半)原子的,但在直接调用它们时就不再是原子的。

多线程本身就已经够难的了。不要仅仅因为你的对象设计被破坏而让它变得更加困难:) Joe Albahari 的“C# 中的线程”对多线程(一般来说,特别是 .NET)有一个很好的介绍,可以在互联网上免费获得(感谢,乔!)。ReaderWriterLockSlim特别是在http://www.albahari.com/threading/part4.aspx#_Reader_Writer_Locks中处理

  • @PavelDurov 不过,这个问题对于堆栈溢出来说并不是一个真正的好问题。您可能想询问程序员,或者只是在互联网上寻找人们以这种或另一种方式争论的人(有些人仍然坚信他们的递归锁,有些人坚决反对)。例如,从我的角度来看,一篇好文章是http://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html。 (2认同)