我想知道使用可升级的读锁相对于执行以下步骤有什么好处:
- Take read lock
- Check condition to see if we need to take write lock
- Release Read Lock
- Take Write Lock
- Perform update
- Release Write Lock
与采取可升级的读锁相反,执行上述步骤的一个明显的缺点是,在步骤3和4之间存在一个时间窗口,其中另一个线程可以占用写锁。
除了这个优点之外,您发现在上述步骤上采取可升级的读取锁定还有哪些其他优点?
最佳答案
让我们考虑一下可以使用没有单独的“可升级读取器”的读取器-写入器锁的不同方式。
如您所指出的,在您的模式下,第3步和第4步之间存在竞争,其中另一个线程可以使用writer锁。更重要的是,在第3步和第4步之间有一个步骤,线程可以使用编写者锁并更改我们在步骤2 中观察到的状态。
因此,根据情况发生的可能性,我们有四个选择:
不难理解为什么4对某些人更具诱惑力,尽管很难看清它如何使死锁更容易产生。可悲的是,稍微难一点就足以让很多人看到优点而看不到缺点。
对于没有发现它的任何人,如果两个线程具有读锁,并且其中一个升级到写锁,则它必须等待另一个线程释放读锁。但是,如果第二个线程在不释放读锁的情况下升级到写锁,则它将在第一个线程上永远等待,这将在第一个线程上永远等待。
如上所述,哪种方法最佳取决于状态在此期间发生变化的可能性(或者,我想我们希望对此做出快速 react )。即使采用不释放升级的最后一种方法也可以在可行的代码中占有一席之地,只要只有一个线程尝试在不释放锁的情况下升级其锁即可。
除了最后一个选项起作用的特殊情况外,其他选项之间的差异都与性能有关,而最有效的性能主要取决于重新检查状态的成本以及由于更改而中止写入的可能性同时。
但是,请注意,所有这些操作都涉及到一个写作者锁,因此,即使确实中止了写入操作,所有这些操作都具有阻塞所有读取线程的作用。
可升级的读锁为我们提供了中间立场,因为尽管它们阻止写锁和其他可升级的读锁,但它们不阻止读锁。尽管它们可能不是更好的读锁,但可以升级为尚未提交写入的写锁。*在决定不升级的情况下,对读线程的影响为零。
这意味着如果线程甚至有可能决定不更改状态,则读取线程也不会受到影响,并且性能的提高肯定可以证明其使用是合理的。
*就此而言,“读者-作家”有点用词不当,我们可以使用
ReaderWriterLockSlim
保护整数或对象数组,使用读取锁原子地读写单个项目,并使用写入锁进行需要读取整个数组而读取时不改变其一部分的操作。在这种情况下,这是需要独占锁的读取操作,而使用共享锁可以进行写操作。
关于c# - 可升级的readlock的优势?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8787606/