Han*_*ney 6 c# multithreading thread-safety readerwriterlockslim
我非常熟悉,ReaderWriterLockSlim
但我EnterUpgradeableReadLock()
最近在一个类中尝试实现...不久之后我意识到当2个或更多线程运行代码时,这几乎肯定是一个保证死锁:
Thread A --> enter upgradeable read lock
Thread B --> enter upgradeable read lock
Thread A --> tries to enter write lock, blocks for B to leave read
Thread B --> tries to enter write lock, blocks for A to leave read
Thread A --> waiting for B to exit read lock
Thread B --> waiting for A to exit read lock
Run Code Online (Sandbox Code Playgroud)
我在这里错过了什么?
编辑
添加了我的场景的代码示例.该Run()
方法将由两个或多个线程同时调用.
public class Deadlocker
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public void Run()
{
_lock.EnterUpgradeableReadLock();
try
{
_lock.EnterWriteLock();
try
{
// Do something
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
}
Run Code Online (Sandbox Code Playgroud)
And*_*own 29
OP之后很长一段时间,但我不同意目前接受的答案.
声明Thread B --> enter upgradeable read lock
不正确.来自文档
任何时候只有一个线程可以处于可升级模式
并回应您的意见:它旨在用于读写模式的非常不同的用法.
TL; DR.可升级模式很有用:
或者,在伪代码中,这里:
// no other writers or upgradeables allowed in here => no race conditions
EnterUpgradeableLock();
if (isWriteRequired()) { EnterWriteLock(); DoWrite(); ExitWriteLock(); }
ExitUpgradeableLock();
Run Code Online (Sandbox Code Playgroud)
给予"更好的表现" ÷比这:
EnterWriteLock(); if (isWriteRequired()) { DoWrite(); } ExitWriteLock();
Run Code Online (Sandbox Code Playgroud)
如果独占锁定部分由于使用SpinLock而需要很长时间,则应小心使用.
可升级锁与SQL服务器SIX锁(与Intent共享以获得exclusive)惊人相似†.
如果没有Intent锁,您必须在eXclusive锁中执行"我应该进行此更改"检查,这会损害并发性.
如果可升级锁可与其他可升级锁共享,则可能与其他可升级锁所有者具有竞争条件.因此,您需要在写锁定内部进行一次检查,从而消除了检查的好处,而不会阻止其他读取操作.
如果我们把所有的锁等待/进入/退出事件的顺序,以及锁定为平行内的工作,那么我们就可以写"大理石"形式的情况下(e
进入; w
等待; x
退出; cr
检查资源; mr
变异资源; R
共享/读取; U
意图/可升级; W
eXclusive/Write):
1--eU--cr--wW----eW--mr--xWxU--------------
2------eR----xR----------------eR--xR------
3--------eR----xR--------------------------
4----wU----------------------eU--cr--xU----
Run Code Online (Sandbox Code Playgroud)
用语言:T1进入Upgradeable/Intent锁.T4等待Upgradeable/Intent锁定.T2和T3输入读锁.T1同时检查资源,赢得比赛并等待eXclusive/Write锁定.T2和T3退出锁.T1进入eXclusive/Write锁定并进行更改.T4进入可升级/意图锁定,不需要进行更改并退出,而不阻止T2,同时执行另一次读取.
可升级锁是:
如果下列之一适用(包括但不限于),则不需要升级:
writelock-check-nowrite-exit
写入器之间的争用率几乎为零(写入条件检查超快) - 即可升级的构造对读取器吞吐量没有帮助;写入锁写入一次的写入器的概率是〜1,因为:
ReadLock-Check-WriteLock-DoubleCheck
是如此之快,它只会导致每万亿次写入失败者; 如果a lock(...){...}
更合适,也不需要它,即:
÷ "性能"由您来定义
† 如果您将锁定对象视为表,并将受保护资源视为层次结构中较低的资源,则此类比近似成立
‡ 读取锁定中的初始检查是可选的,可升级锁定内的检查是强制性的,因此可以单个或双重检查模式使用.