ReaderWriterLock クラス

定義

1 つのライターと複数のリーダーをサポートするロックを定義します。

public ref class ReaderWriterLock sealed : System::Runtime::ConstrainedExecution::CriticalFinalizerObject
public ref class ReaderWriterLock sealed
public sealed class ReaderWriterLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject
public sealed class ReaderWriterLock
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class ReaderWriterLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject
type ReaderWriterLock = class
    inherit CriticalFinalizerObject
type ReaderWriterLock = class
[<System.Runtime.InteropServices.ComVisible(true)>]
type ReaderWriterLock = class
    inherit CriticalFinalizerObject
Public NotInheritable Class ReaderWriterLock
Inherits CriticalFinalizerObject
Public NotInheritable Class ReaderWriterLock
継承
ReaderWriterLock
継承
ReaderWriterLock
属性

次の例では、 ReaderWriterLock を使用して、複数のスレッドによって同時に読み取られ、排他的に書き込まれる、 resourceという名前の整数値である共有リソースを保護する方法を示します。 ReaderWriterLockはクラス レベルで宣言され、すべてのスレッドに表示されることに注意してください。

// The complete code is located in the ReaderWriterLock class topic.
using System;
using System.Threading;

public class Example
{
   static ReaderWriterLock rwl = new ReaderWriterLock();
   // Define the shared resource protected by the ReaderWriterLock.
   static int resource = 0;

   const int numThreads = 26;
   static bool running = true;

   // Statistics.
   static int readerTimeouts = 0;
   static int writerTimeouts = 0;
   static int reads = 0;
   static int writes = 0;

   public static void Main()
   {
      // Start a series of threads to randomly read from and
      // write to the shared resource.
      Thread[] t = new Thread[numThreads];
      for (int i = 0; i < numThreads; i++){
         t[i] = new Thread(new ThreadStart(ThreadProc));
         t[i].Name = new String((char)(i + 65), 1);
         t[i].Start();
         if (i > 10)
            Thread.Sleep(300);
      }

      // Tell the threads to shut down and wait until they all finish.
      running = false;
      for (int i = 0; i < numThreads; i++)
         t[i].Join();

      // Display statistics.
      Console.WriteLine("\n{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
            reads, writes, readerTimeouts, writerTimeouts);
      Console.Write("Press ENTER to exit... ");
      Console.ReadLine();
   }

   static void ThreadProc()
   {
      Random rnd = new Random();

      // Randomly select a way for the thread to read and write from the shared
      // resource.
      while (running) {
         double action = rnd.NextDouble();
         if (action < .8)
            ReadFromResource(10);
         else if (action < .81)
            ReleaseRestore(rnd, 50);
         else if (action < .90)
            UpgradeDowngrade(rnd, 100);
         else
            WriteToResource(rnd, 100);
      }
   }

   // Request and release a reader lock, and handle time-outs.
   static void ReadFromResource(int timeOut)
   {
      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It is safe for this thread to read from the shared resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref reads);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // Request and release the writer lock, and handle time-outs.
   static void WriteToResource(Random rnd, int timeOut)
   {
      try {
         rwl.AcquireWriterLock(timeOut);
         try {
            // It's safe for this thread to access from the shared resource.
            resource = rnd.Next(500);
            Display("writes resource value " + resource);
            Interlocked.Increment(ref writes);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseWriterLock();
         }
      }
      catch (ApplicationException) {
         // The writer lock request timed out.
         Interlocked.Increment(ref writerTimeouts);
      }
   }

   // Requests a reader lock, upgrades the reader lock to the writer
   // lock, and downgrades it to a reader lock again.
   static void UpgradeDowngrade(Random rnd, int timeOut)
   {
      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It's safe for this thread to read from the shared resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref reads);

            // To write to the resource, either release the reader lock and
            // request the writer lock, or upgrade the reader lock. Upgrading
            // the reader lock puts the thread in the write queue, behind any
            // other threads that might be waiting for the writer lock.
            try {
               LockCookie lc = rwl.UpgradeToWriterLock(timeOut);
               try {
                  // It's safe for this thread to read or write from the shared resource.
                  resource = rnd.Next(500);
                  Display("writes resource value " + resource);
                  Interlocked.Increment(ref writes);
               }
               finally {
                  // Ensure that the lock is released.
                  rwl.DowngradeFromWriterLock(ref lc);
               }
            }
            catch (ApplicationException) {
               // The upgrade request timed out.
               Interlocked.Increment(ref writerTimeouts);
            }

            // If the lock was downgraded, it's still safe to read from the resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref reads);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // Release all locks and later restores the lock state.
   // Uses sequence numbers to determine whether another thread has
   // obtained a writer lock since this thread last accessed the resource.
   static void ReleaseRestore(Random rnd, int timeOut)
   {
      int lastWriter;

      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It's safe for this thread to read from the shared resource,
            // so read and cache the resource value.
            int resourceValue = resource;     // Cache the resource value.
            Display("reads resource value " + resourceValue);
            Interlocked.Increment(ref reads);

            // Save the current writer sequence number.
            lastWriter = rwl.WriterSeqNum;

            // Release the lock and save a cookie so the lock can be restored later.
            LockCookie lc = rwl.ReleaseLock();

            // Wait for a random interval and then restore the previous state of the lock.
            Thread.Sleep(rnd.Next(250));
            rwl.RestoreLock(ref lc);

            // Check whether other threads obtained the writer lock in the interval.
            // If not, then the cached value of the resource is still valid.
            if (rwl.AnyWritersSince(lastWriter)) {
               resourceValue = resource;
               Interlocked.Increment(ref reads);
               Display("resource has changed " + resourceValue);
            }
            else {
               Display("resource has not changed " + resourceValue);
            }
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // Helper method briefly displays the most recent thread action.
   static void Display(string msg)
   {
      Console.Write("Thread {0} {1}.       \r", Thread.CurrentThread.Name, msg);
   }
}
' The complete code is located in the ReaderWriterLock class topic.
Imports System.Threading

Public Module Example
   Private rwl As New ReaderWriterLock()
   ' Define the shared resource protected by the ReaderWriterLock.
   Private resource As Integer = 0

   Const numThreads As Integer = 26
   Private running As Boolean = True
   
   ' Statistics.
   Private readerTimeouts As Integer = 0
   Private writerTimeouts As Integer = 0
   Private reads As Integer = 0
   Private writes As Integer = 0
  
   Public Sub Main()
      ' Start a series of threads to randomly read from and
      ' write to the shared resource.
      Dim t(numThreads - 1) As Thread
      Dim i As Integer
      For i = 0 To numThreads - 1
         t(i) = New Thread(New ThreadStart(AddressOf ThreadProc))
         t(i).Name = Chr(i + 65)
         t(i).Start()
         If i > 10 Then
            Thread.Sleep(300)
         End If
      Next

      ' Tell the threads to shut down and wait until they all finish.
      running = False
      For i = 0 To numThreads - 1
         t(i).Join()
      Next
      
      ' Display statistics.
      Console.WriteLine(vbCrLf & "{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
                        reads, writes, readerTimeouts, writerTimeouts)
      Console.Write("Press ENTER to exit... ")
      Console.ReadLine()
   End Sub

   Sub ThreadProc()
      Dim rnd As New Random

      ' Randomly select a way for the thread to read and write from the shared
      ' resource.
      While running
         Dim action As Double = rnd.NextDouble()
         If action < 0.8 Then
            ReadFromResource(10)
         ElseIf action < 0.81 Then
            ReleaseRestore(rnd, 50)
         ElseIf action < 0.9 Then
            UpgradeDowngrade(rnd, 100)
         Else
            WriteToResource(rnd, 100)
         End If
      End While
   End Sub
    
   ' Request and release a reader lock, and handle time-outs.
   Sub ReadFromResource(timeOut As Integer)
      Try
         rwl.AcquireReaderLock(timeOut)
         Try
            ' It's safe for this thread to read from the shared resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)
         Finally
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try
   End Sub

   ' Request and release the writer lock, and handle time-outs.
   Sub WriteToResource(rnd As Random, timeOut As Integer)
      Try
         rwl.AcquireWriterLock(timeOut)
         Try
            ' It's safe for this thread to read or write from the shared resource.
            resource = rnd.Next(500)
            Display("writes resource value " & resource)
            Interlocked.Increment(writes)
         Finally
            ' Ensure that the lock is released.
            rwl.ReleaseWriterLock()
         End Try
      Catch ex As ApplicationException
         ' The writer lock request timed out.
         Interlocked.Increment(writerTimeouts)
      End Try
   End Sub

   ' Requests a reader lock, upgrades the reader lock to the writer
   ' lock, and downgrades it to a reader lock again.
   Sub UpgradeDowngrade(rnd As Random, timeOut As Integer)
      Try
         rwl.AcquireReaderLock(timeOut)
         Try
            ' It's safe for this thread to read from the shared resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)
            
            ' To write to the resource, either release the reader lock and
            ' request the writer lock, or upgrade the reader lock. Upgrading
            ' the reader lock puts the thread in the write queue, behind any
            ' other threads that might be waiting for the writer lock.
            Try
               Dim lc As LockCookie = rwl.UpgradeToWriterLock(timeOut)
               Try
                  ' It's safe for this thread to read or write from the shared resource.
                  resource = rnd.Next(500)
                  Display("writes resource value " & resource)
                  Interlocked.Increment(writes)
               Finally
                  ' Ensure that the lock is released.
                  rwl.DowngradeFromWriterLock(lc)
               End Try
            Catch ex As ApplicationException
               ' The upgrade request timed out.
               Interlocked.Increment(writerTimeouts)
            End Try
            
            ' If the lock was downgraded, it's still safe to read from the resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)
         Finally
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try
   End Sub

   ' Release all locks and later restores the lock state.
   ' Uses sequence numbers to determine whether another thread has
   ' obtained a writer lock since this thread last accessed the resource.
   Sub ReleaseRestore(rnd As Random ,timeOut As Integer)
      Dim lastWriter As Integer
      
      Try
         rwl.AcquireReaderLock(timeOut)
         Try
            ' It's safe for this thread to read from the shared resource,
            ' so read and cache the resource value.
            Dim resourceValue As Integer = resource
            Display("reads resource value " & resourceValue)
            Interlocked.Increment(reads)
            
            ' Save the current writer sequence number.
            lastWriter = rwl.WriterSeqNum
            
            ' Release the lock and save a cookie so the lock can be restored later.
            Dim lc As LockCookie = rwl.ReleaseLock()
            
            ' Wait for a random interval and then restore the previous state of the lock.
            Thread.Sleep(rnd.Next(250))
            rwl.RestoreLock(lc)
           
            ' Check whether other threads obtained the writer lock in the interval.
            ' If not, then the cached value of the resource is still valid.
            If rwl.AnyWritersSince(lastWriter) Then
               resourceValue = resource
               Interlocked.Increment(reads)
               Display("resource has changed " & resourceValue)
            Else
               Display("resource has not changed " & resourceValue)
            End If
         Finally
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try
   End Sub

   ' Helper method briefly displays the most recent thread action.
   Sub Display(msg As String)
      Console.Write("Thread {0} {1}.       " & vbCr, Thread.CurrentThread.Name, msg)
   End Sub
End Module

注釈

Important

.NET Framework には、ReaderWriterLockSlimReaderWriterLock の 2 つのリーダー ライター ロックがあります。 ReaderWriterLockSlim は、すべての新しい開発に推奨されます。 ReaderWriterLockSlimReaderWriterLockに似ていますが、再帰とロック状態のアップグレードとダウングレードの規則が簡略化されています。 ReaderWriterLockSlim は、デッドロックの可能性がある多くのケースを回避します。 さらに、 ReaderWriterLockSlim のパフォーマンスは、 ReaderWriterLockよりも大幅に優れています。

ReaderWriterLock は、リソースへのアクセスを同期するために使用されます。 任意の時点で、複数のスレッドに対する同時読み取りアクセスまたは単一スレッドの書き込みアクセスを許可します。 リソースが頻繁に変更される頻度が低い状況では、 ReaderWriterLock は、 Monitorなどの単純な一度に 1 回限りのロックよりも優れたスループットを提供します。

ReaderWriterLock は、ほとんどのアクセスが読み取りであるのに対し、書き込みの頻度が低く、期間が短い場合に最適です。 複数のリーダーが単一のライターと交互に使用されるため、リーダーもライターも長期間ブロックされません。

リーダー ロックまたはライター ロックを長期間保持すると、他のスレッドが不足します。 最適なパフォーマンスを得るために、書き込みの期間を最小限に抑えるために、アプリケーションの再構築を検討してください。

スレッドはリーダー ロックまたはライター ロックを保持できますが、両方を同時に保持することはできません。 ライター ロックを取得するためにリーダー ロックを解放する代わりに、 UpgradeToWriterLockDowngradeFromWriterLockを使用できます。

再帰的なロック要求は、ロックのロック数を増やします。

リーダーとライターは個別にキューに入れられます。 スレッドがライター ロックを解放すると、その時点でリーダー キューで待機しているすべてのスレッドにリーダー ロックが付与されます。これらのリーダー ロックがすべて解放されると、ライター キューで待機している次のスレッド (存在する場合) にライター ロックが付与されます。 言い換えると、 ReaderWriterLock はリーダーのコレクションと 1 つのライターを交互に使用します。

ライター キュー内のスレッドがアクティブなリーダー ロックの解放を待機している間に、新しいリーダー ロックを要求するスレッドがリーダー キューに蓄積されます。 既存のリーダー ロック 所有者と同時アクセスを共有できる場合でも、要求は許可されません。これは、読者による無期限のブロックからライターを保護するのに役立ちます。

ReaderWriterLockのロックを取得するためのほとんどのメソッドは、タイムアウト値を受け入れます。 アプリケーションでデッドロックを回避するには、タイムアウトを使用します。 たとえば、スレッドが 1 つのリソースでライター ロックを取得し、2 つ目のリソースに対してリーダー ロックを要求する場合があります。その間、別のスレッドが 2 番目のリソースのライター ロックを取得し、最初のリソースに対してリーダー ロックを要求する場合があります。 タイムアウトを使用しない限り、スレッドはデッドロックします。

タイムアウト間隔の有効期限が切れ、ロック要求が許可されていない場合、メソッドは ApplicationExceptionをスローすることによって呼び出し元のスレッドに制御を返します。 スレッドは、この例外をキャッチし、次に実行するアクションを決定できます。

タイムアウトはミリ秒単位で表されます。 System.TimeSpanを使用してタイムアウトを指定する場合、使用される値は、TimeSpanで表されるミリ秒単位の合計数です。 次の表は、有効なタイムアウト値をミリ秒単位で示しています。

Value Description
-1 スレッドは、ロックが取得されるまで待機します。時間はかかりません。 整数タイムアウトを指定するメソッドの場合は、定数 Infinite を使用できます。
0 スレッドはロックの取得を待機しません。 ロックをすぐに取得できない場合、メソッドは返します。
>0 待機するミリ秒数。

-1 を除き、負のタイムアウト値は使用できません。 -1 以外の負の整数を指定すると、代わりにタイムアウト値 0 が使用されます。 (つまり、ロックをすぐに取得できない場合は、待機せずにメソッドが返されます)。-1 以外の負のミリ秒数を表す TimeSpan を指定すると、 ArgumentOutOfRangeException がスローされます。

コンストラクター

名前 説明
ReaderWriterLock()

ReaderWriterLock クラスの新しいインスタンスを初期化します。

プロパティ

名前 説明
IsReaderLockHeld

現在のスレッドがリーダー ロックを保持しているかどうかを示す値を取得します。

IsWriterLockHeld

現在のスレッドがライター ロックを保持しているかどうかを示す値を取得します。

WriterSeqNum

現在のシーケンス番号を取得します。

メソッド

名前 説明
AcquireReaderLock(Int32)

タイムアウトの Int32 値を使用して、リーダー ロックを取得します。

AcquireReaderLock(TimeSpan)

タイムアウトの TimeSpan 値を使用して、リーダー ロックを取得します。

AcquireWriterLock(Int32)

タイムアウトの Int32 値を使用して、ライター ロックを取得します。

AcquireWriterLock(TimeSpan)

タイムアウトの TimeSpan 値を使用して、ライター ロックを取得します。

AnyWritersSince(Int32)

シーケンス番号が取得されてから、ライター ロックが任意のスレッドに付与されているかどうかを示します。

DowngradeFromWriterLock(LockCookie)

スレッドのロック状態を、 UpgradeToWriterLock(Int32) が呼び出される前の状態に復元します。

Equals(Object)

指定されたオブジェクトが現在のオブジェクトと等しいかどうかを判断します。

(継承元 Object)
Finalize()

ガベージ コレクターが ReaderWriterLock オブジェクトを再利用するときに、リソースが解放され、その他のクリーンアップ操作が実行されるようにします。

GetHashCode()

既定のハッシュ関数として機能します。

(継承元 Object)
GetType()

現在のインスタンスの Type を取得します。

(継承元 Object)
MemberwiseClone()

現在の Objectの簡易コピーを作成します。

(継承元 Object)
ReleaseLock()

スレッドがロックを取得した回数に関係なく、ロックを解放します。

ReleaseReaderLock()

ロック カウントをデクリメントします。

ReleaseWriterLock()

ライター ロックのロック数をデクリメントします。

RestoreLock(LockCookie)

スレッドのロック状態を、 ReleaseLock()呼び出す前の状態に復元します。

ToString()

現在のオブジェクトを表す文字列を返します。

(継承元 Object)
UpgradeToWriterLock(Int32)

タイムアウトの Int32 値を使用して、リーダー ロックをライター ロックにアップグレードします。

UpgradeToWriterLock(TimeSpan)

タイムアウトの TimeSpan 値を使用して、リーダー ロックをライター ロックにアップグレードします。

適用対象

スレッド セーフ

この型はスレッド セーフです。

こちらもご覧ください