2012-01-09 33 views
5

我想知道什么是使用可升级读锁,而不是执行这些步骤的优点:可升级的readlock的优点?

  1. 就拿读锁
  2. 检查条件,看看我们是否需要采取写锁
  3. 发布读锁定
  4. 就拿写锁定
  5. 进行更新
  6. 释放写锁定
  7. 的执行上述步骤

一个明显的缺点,而不是采取可升级读锁是,存在的时间的步骤3和4,在那里另一个线程可能需要写锁定之间的窗口。

除了这个优点,你还发现在上面提到的步骤中采用可升级的读锁定的其他优点吗?

+0

我敢打赌,这是可升级锁的最大优势,但我期待着其他意见。+1 – 2012-01-09 11:30:48

回答

6

让我们考虑一下可以使用不具有单独的“可升级阅读器”的读写器锁的不同方式。

使用图案,有3及4正如你指出,在另一个线程可以采取的作家锁之间的竞争。更重要的是,在3到4之间有一个步骤,一个线程可以采用写入锁定并更改我们在步骤2中观察到的状态

因此,我们有四个选择取决于如何LIKEY这是发生:

  1. 我们留在你的做法,因为这实际上是不可能(例如,一个给定的状态转换是单向的我们应用程序,所以一旦观察它是永久性的)。在这种情况下,尽管我们很可能已经改造过,所以根本不需要锁。 (单向转换适合无锁技术)。

  2. 我们只是把作家锁定在第一位,因为我们在步骤2中观察到的状态很可能会改变,并且使用读卡器锁定检查它的时间会很浪费。

  3. 我们改变你的步骤:

    1. 就拿读锁
    2. 检查条件,看看我们是否需要采取写锁
    3. 释放读锁定
    4. 就拿写锁定
    5. 回复 - 检查条件是否改变。
    6. 进行更新
    7. 释放写锁定
  4. 我们更改为:

    1. 采取一个递归支撑锁的读锁。
    2. 检查我们是否需要写入锁定。
    3. 取写锁定(不释放读取)。
    4. 执行更新。
    5. 释放写入锁定。
    6. 释放读取锁定。

不难看出为什么4更诱人一些,虽然它只是稍微很难看到它是如何使死锁容易创建。令人遗憾的是,稍微难一点的就足以让很多人看到优点而不会看到缺点。

对于任何没有发现它的人来说,如果两个线程有​​读锁定,并且其中一个线程升级到写锁定,它必须等待另一个线程释放读锁定。但是,如果第二个线程在不释放读锁的情况下升级到写锁,那么它将在第一个线程上永远等待,永久等待它。


正如上面所说,只是哪种方法最好取决于它有多大的可能性为国家在此期间(或如何及时,我们要对其做出反应,我想)改变。即使最后一种非释放升级的方法可以在可行的代码中占据一席之地,只要有一个线程曾试图升级其锁而不释放。

除了最后一个选项有效的特殊情况之外,其他选项之间的差别都与性能有关,哪个性能最高取决于重新检查状态的成本以及写入中止的可能性由于其间的变化。

但是,请注意,它们都涉及到一个写入锁定,因此它们都具有阻止所有读取线程的效果,即使写入确实被中止。

可升级的读锁给了我们一个中间地带,因为虽然它们阻止了写锁和其他可升级的读锁,但它们不会阻塞读锁。它们或许更好,但不是作为可以升级的写锁,而是可以升级为尚未写入的写锁。*在决定不升级的情况下,对读线程的影响为零。

这意味着如果线程有可能决定不更改状态,读线程不会受到影响,并且性能改进可以证明它的用途。

*对于这个问题,“读者 - 写作者”有点用词不当,我们可能会用ReaderWriterLockSlim保护整数或对象的数组,使用读锁来以原子方式读取和写入单个项目,并将写锁用于需要读取整个数组而不使其部分在读取时发生变化的操作。在这种情况下,它是一种阅读操作,而不是需要排他锁,而写入操作对于共享锁则没有问题。

+0

感谢乔恩,非常好的观点。 – Ngm 2012-01-11 05:49:07

0

它还可以防止可能发生的死锁,因为不同的线程同时运行,并且彼此等待释放锁。

+2

你能为此提供一个例子吗?我们也不能说我上面指出的步骤是一样的吗? – Ngm 2012-01-09 11:39:22

+0

升级锁没有您提到的竞争条件。 – linkerro 2012-01-09 13:44:41

+0

没有竞争条件,因为他们争夺相同的资源(让我们说r1),只有一个胜利,没有死锁。我没有看到谁会防止死锁? – Ngm 2012-01-10 06:37:03