2

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

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

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

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

这里如何实现死锁?例如,如果我在已经获取 WriteReadLock 时尝试调用 EnterReadLock(并且我在 SupportsRecursion 策略下),我会得到一个异常......

4

1 回答 1

4

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

这样做的主要问题是,首先要解决这种情况,您可能对谁处理必要的同步有严重的问题——您的锁可能太细化或太全局化。多线程是困难的,而让它变得更加困难是完全愚蠢的。

第二个大问题是锁与线程相关联。但是,如果您正在编写异步代码,您的代码可能会在不同的线程之间随意跳转,这可能意味着看起来正在使用递归锁的代码不是 - 外部锁最终由不同的线程拥有比内部锁,你永远死锁与线程 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中处理

于 2016-06-09T08:54:37.510 回答