ReaderWriterLockSlimクラス

これはReaderWriterLockを簡素化したクラスであり、それよりもパフォーマンスに優れています。Remarks - ReaderWriterLockSlim Class (System.Threading) | Microsoft Learn

コンストラクタ

public ReaderWriterLockSlim (System.Threading.LockRecursionPolicy recursionPolicy);
ReaderWriterLockSlim(LockRecursionPolicy) - ReaderWriterLockSlim Constructor (System.Threading) | Microsoft Learn

recursionPolicyを指定しないコンストラクタでは既定でLockRecursionPolicy.NoRecursionとなり、同一のスレッドからはロックを再帰的に取得できません。

不要な複雑さやデッドロックの傾向を防ぐため、再帰を無効することが推奨されています。ただしMonitorやReaderWriterLockを使用する既存のコードからの移行を簡単にするには、これを有効にします。Remarks - ReaderWriterLockSlim Class (System.Threading) | Microsoft Learn

LockRecursionPolicy.SupportsRecursionを指定すれば、再帰的に取得できます。

ReaderWriterLockSlim rwLockSlim1 = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
rwLockSlim1.EnterReadLock();
rwLockSlim1.EnterReadLock(); // ok

指定しないと、ロックを再帰的に取得しようとしたときに例外が投げられます。

ReaderWriterLockSlim rwLockSlim2 = new ReaderWriterLockSlim();
rwLockSlim2.EnterReadLock();
rwLockSlim2.EnterReadLock(); // LockRecursionException「このモードでは、再帰的な読み取りロックは取得できません。」

ReaderWriterLockSlim rwLockSlim3 = new ReaderWriterLockSlim();
rwLockSlim3.EnterWriteLock();
rwLockSlim3.EnterReadLock(); // LockRecursionException「このモードでは、書き込みロックを保持しながら読み取りロックを取得することはできません。」

ReaderWriterLockSlim rwLockSlim4 = new ReaderWriterLockSlim();
rwLockSlim4.EnterReadLock();
rwLockSlim4.EnterWriteLock(); // LockRecursionException「読み取りロックを保持しながら、書き込みロックを取得することはできません。このパターンでは、デッドロックの可能性が高まります。書き込みロックを取得する前に、読み取りロックが解放されていることを確認してください。アップグレードが必要な場合は、読み取りロックの代わりに、アップグレード可能なロックを使用します。」

これは同一のスレッドでの制約であり、異なるスレッドからは取得できます。

ReaderWriterLockSlim rwLockSlim5 = new ReaderWriterLockSlim();
rwLockSlim5.EnterReadLock();

Task.Run(() =>
{
    rwLockSlim5.EnterReadLock(); // ok
}).Wait();

このクラスはIDisposableを実装するため、不要になったら破棄する必要があります。Remarks - ReaderWriterLockSlim Class (System.Threading) | Microsoft Learn

プロパティ

プロパティ 内容
bool IsReadLockHeld trueならば、現在のスレッドが読み取りモードに入っている
bool IsWriteLockHeld trueならば、現在のスレッドが書き込みモードに入っている
bool IsUpgradeableReadLockHeld trueならば、現在のスレッドがアップグレード可能モードに入っている
int CurrentReadCount 読み取りモードでロックに入った、スレッドの総数
int WaitingReadCount 読み取りモードに入るのを待機している、スレッドの総数
int WaitingWriteCount 書き込みモードに入るのを待機している、スレッドの総数
int WaitingUpgradeCount アップグレード可能モードに入るのを待機している、スレッドの総数
int RecursiveReadCount 現在のスレッドが読み取りモードでロックに入った、再帰としての回数
  • 0 … ロックに入っていない
  • 1 … ロックに入っているが、再帰ではない
  • n … n-1回、再帰としてロックに入っている
int RecursiveWriteCount  
int RecursiveUpgradeCount  
     
プロパティ - ReaderWriterLockSlim クラス (System.Threading) | Microsoft Learn
ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();
bool lock1 = rwLockSlim.IsReadLockHeld;   // false
int count1 = rwLockSlim.CurrentReadCount; // 0

rwLockSlim.EnterReadLock();
bool lock2 = rwLockSlim.IsReadLockHeld;   // true
int count2 = rwLockSlim.CurrentReadCount; // 1

メソッド

EnterReadLock()

読み取りモードでのロックの開始を試みさせられます。

public void EnterReadLock ();
ReaderWriterLockSlim.EnterReadLock Method (System.Threading) | Microsoft Learn

同時に複数のスレッドが読み取りモードになれます。

1つ以上のスレッドが書き込みモードでのロックを待っているとき、それがタイムアウトするか、ロックしてから抜けるまで、呼び出しスレッドはブロックされます。そのとき制御が戻らない恐れがあるため、TryEnterReadLock()でタイムアウトを指定するのが安全です。

他のスレッドが読み取りモードのとき、1つのスレッドを昇格可能モードにできます。

ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();
rwLockSlim.EnterReadLock();
try
{
    // 共有リソースからの読み取り
}
finally
{
    rwLockSlim.ExitReadLock();
}

EnterWriteLock()

書き込みモードでのロックの開始を試みさせられます。

public void EnterWriteLock ();
ReaderWriterLockSlim.EnterWriteLock Method (System.Threading) | Microsoft Learn
ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();
rwLockSlim.EnterWriteLock();
try
{
    // 共有リソースへの書き込み
}
finally
{
    rwLockSlim.ExitWriteLock();
}

このメソッドはロックを得られるまで呼び出し元のスレッドをブロックするため、デッドロックすることがあります。

ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();
rwLockSlim.EnterWriteLock();

Task.Run(() =>
{
    rwLockSlim.EnterWriteLock(); // ロックは解放されないため、ここでデッドロックする

    rwLockSlim.ExitWriteLock();
}).Wait(); // このスレッドの終了を、ここで待機する

rwLockSlim.ExitWriteLock(); // ここへは到達しない

TryEnterReadLock()

タイムアウトを指定して、読み取りモードでのロックを試みさせられます。

public bool TryEnterReadLock (
    int millisecondsTimeout // 最大の待ち時間
    );
TryEnterReadLock(Int32) - ReaderWriterLockSlim.TryEnterReadLock Method (System.Threading) | Microsoft Learn

読み取りモードに入れたならば、trueが返されます。

ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();

int timeout = 10;
if (rwLockSlim.TryEnterReadLock(timeout))
{
    try
    {
        // 共有リソースへの書き込み
    }
    finally
    {
        rwLockSlim.ExitReadLock();
    }
}

EnterUpgradeableReadLock()

昇格可能モード (upgradeable mode / アップグレード可能モード) でのロックを試みさせられます。

public void EnterUpgradeableReadLock ();
ReaderWriterLockSlim.EnterUpgradeableReadLock Method (System.Threading) | Microsoft Learn

このメソッドはロックを得るまで呼び出しスレッドをブロックするため、TryEnterUpgradeableReadLock()を用いるのが安全です。

スレッドがアクセスするリソースが、通常は読み取りモードで保護されているときに、昇格可能モードを使用します。昇格可能モードのスレッドは、読み取りモードに降格 (downgrade / ダウングレード) させたり、書き込みモードに昇格 (upgrade / アップグレード) させたりできます。

一度に昇格可能モードに入れるスレッドは、1つだけです。昇格可能モードのロックを待っているスレッドがあっても、書き込みモードのロックを待っているスレッドがなければ、他のスレッドは読み取りモードに入れます。

Upgrading and Downgrading Locks - ReaderWriterLockSlim Class (System.Threading) | Microsoft Learn

ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();
rwLockSlim.EnterUpgradeableReadLock();
try
{
    string result = null;

    rwLockSlim.EnterReadLock();
    try
    {
        result = // 共有リソースからの読み取り
    }
    finally
    {
        rwLockSlim.ExitReadLock();
    }

    if (result == null)
    {
        result = // 初期化

        rwLockSlim.EnterWriteLock();
        try
        {
            // 共有リソースへの書き込み
        }
        finally
        {
            rwLockSlim.ExitWriteLock();
        }
    }
}
finally
{
    rwLockSlim.ExitUpgradeableReadLock();
}

Dispose()

ReaderWriterLockSlimクラスのインスタンスによって使用されている、すべてのリソースを解放できます。

public void Dispose ();
ReaderWriterLockSlim.Dispose Method (System.Threading) | Microsoft Learn

Dispose - ReaderWriterLockSlim.Dispose

Microsoft Learnから検索