2

一連の法的措置の後、ReaderWriterLockSlim が壊れたように見えるシナリオに遭遇しました。

フローは次のとおりです。

  1. スレッド 1 はライター ロック 1 を取得します
  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

これはフレームワークのバグのようです(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 に答える
1

リーダーが読み取りロックに入ることはありません。書き込みが解放されるのを待っています。中断されると、入力していなくても終了しようとするため、読み取りカウントが0未満になると思います:)

于 2013-03-17T13:59:46.377 に答える
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);
}

実行すると、デバッグ出力は、取得されている書き込みロック、取得されていない最初の読み取りロック、および取得された 2 番目の読み取りロックを正しく示します。

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 に答える