2

我遇到了 ReaderWriterLockSlim 在一系列法律行动后似乎被破坏的场景。

流程是:

  1. 线程 1 需要写入器 lock1
  2. 线程 2 尝试获取读卡器 lock1 - 块
  3. 线程 2 被中断,调用 lock1.ExitReadLock 线程 2 没有获得锁。似乎应该抛出异常。
  4. 线程 1 退出 lock1 的写入者锁
  5. 任何试图获取 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);
}
4

3 回答 3

1

读者永远不会进入读锁。它等待写入被释放。当它被中断时,即使您从未进入过,您也会尝试退出,导致读取计数低于 0 我想:)

于 2013-03-17T13:59:46.377 回答
1

这看起来像是框架中的一个错误(在 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

(事实上​​,如果在之前进入锁的任何线程上ExitReadLock()没有匹配就调用它会破坏锁。)EnterReadLock()

仅当ReaderWriterLockSlim使用无参数构造函数或使用LockRecursionPolicy.NoRecursion. 如果用创建LockRecursionPolicy.SupportsRecursion,它不会被不匹配的破坏ExitReadLock()

如果您希望读取器线程在等待进入锁定时被中断,我建议将读取器线程方法更改为:

var reader = new Thread(() =>
{
    var entered = false;
    try
    {
        rwlock.EnterReadLock();
        entered = true;
        s_logger.Info("Enter");
    }
    finally
    {
        if (entered) rwlock.ExitReadLock();
    }
});
于 2013-03-17T17:40:26.033 回答
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);
}

运行时,调试输出正确显示写入锁被占用,第一个读取锁未占用,第二个读取锁被占用:

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
于 2013-03-17T14:18:09.240 回答