我遇到了一个场景,ReaderWriterLockSlim在一系列法律诉讼后似乎被破坏了。
流程为:
- 线程 1 采用写入器锁 1
- 线程 2 尝试获取读取器锁定 1 - 块
- 线程 2 中断,并调用 lock1。退出读取锁定线程 2 未获得锁。似乎应该抛出一个例外。
- 线程 1 退出锁 1 的写入器锁
- 任何尝试获取 lock1 的线程。EnterReadLock将永远阻止
在上述阶段 3 之后,调试器将显示该 lock1。CurrentReadCount 已损坏 - 似乎已溢出到0x7FFFFFF。
我想知道是否有人遇到过这种情况,或者我错过了什么。
重现它的代码:
[TestMethod]
public void ReproTest()
{
var rwlock = new ReaderWriterLockSlim();
rwlock.EnterWriteLock();
bool taken = false;
var reader = new Thread(() =>
{
try
{
rwlock.EnterReadLock();
s_logger.Info("Enter");
}
catch (ThreadInterruptedException)
{
rwlock.ExitReadLock();
}
});
reader.Name = "Reader";
reader.Start();
Thread.Sleep(1000);
reader.Interrupt();
Thread.Sleep(1000);
rwlock.ExitWriteLock();
while (!taken)
{
taken = rwlock.TryEnterReadLock(1000);
}
Thread.Sleep(1000);
}
这看起来像框架中的一个错误(在 v3.5 和 v4.0 上测试(。ExitReadLock()
应该抛出一个SynchronizationLockException
,但在这种情况下不会。实际上,您可以通过以下方法更轻松地触发非常相似的问题:
rwlock.EnterReadLock();
rwlock.ExitReadLock();
// This should throw a SynchronizationLockException but doesn't
rwlock.ExitReadLock();
// At this point, rwlock.CurrentReaderCount = 0x0fffffff
(事实上,如果在之前进入锁的任何线程上没有匹配EnterReadLock()
的情况下调用锁,ExitReadLock()
会损坏锁。
仅当使用无参数构造函数或使用 LockRecursionPolicy.NoRecursion
创建ReaderWriterLockSlim
时,才会出现此问题。如果用LockRecursionPolicy.SupportsRecursion
创建,它不会被不匹配的ExitReadLock()
破坏。
等待进入锁定时被中断,我建议将读取器线程方法更改为:
var reader = new Thread(() =>
{
var entered = false;
try
{
rwlock.EnterReadLock();
entered = true;
s_logger.Info("Enter");
}
finally
{
if (entered) rwlock.ExitReadLock();
}
});
读取器永远不会进入读锁定。 它坐在那里等待写入被释放。 当它被中断时,即使你从未进入过,你也会尝试退出,导致读取计数低于 0 我想:)
@Lasse和@Jeremy指出的内容的代码:
static public void ReproTest()
{
var rwlock = new ReaderWriterLockSlim();
rwlock.EnterWriteLock();
s_logger.Info("0:Enter");
bool taken1 = false;
var reader = new Thread(() =>
{
try
{
rwlock.EnterReadLock();
s_logger.Info("1:Enter");
// only set to true if taken
taken1 = true;
}
catch (ThreadInterruptedException)
{
// only release if taken
if (taken1)
rwlock.ExitReadLock();
taken1 = false;
}
});
reader.Name = "Reader";
reader.Start();
Thread.Sleep(1000);
reader.Interrupt();
Thread.Sleep(1000);
rwlock.ExitWriteLock();
// 2nd taken variable here only so we can see state of taken1
bool taken2 = taken1;
while (!taken2)
{
taken2 = rwlock.TryEnterReadLock(1000);
s_logger.Info("2:Enter");
}
Thread.Sleep(1000);
}
运行时,调试输出正确显示正在获取的写锁定、未创建的第 1 个读锁定和已采用的第二个读锁定:
0:Enter
A first chance exception of type 'System.Threading.ThreadInterruptedException' occurred in mscorlib.dll
The thread 'Reader' (0x1358) has exited with code 0 (0x0).
2:Enter