ReaderWriterLock 類別

定義

定義一個支援單一寫入者與多個讀取器的鎖。

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 框架有兩個讀寫鎖定,分別是 ReaderWriterLockSlimReaderWriterLockReaderWriterLockSlim 被建議用於所有新的開發。 ReaderWriterLockSlim 類似於 ReaderWriterLock,但它已簡化遞歸規則,以及升級和降級鎖定狀態的規則。 ReaderWriterLockSlim 可避免許多潛在的死結案例。 此外,效能 ReaderWriterLockSlim 明顯優於 ReaderWriterLock

ReaderWriterLock 用於同步對資源的存取。 在任何時刻,它允許多個執行緒同時讀取存取,或單一執行緒的寫入存取。 在資源變動不頻繁的情況下,a ReaderWriterLock 提供比單純一次一封鎖更好的吞吐量,例如 Monitor

ReaderWriterLock 在大多數存取是讀取、寫入較少且持續時間短的情況下效果最佳。 多位讀取器與單一寫入者交替使用,避免讀者與寫入者長時間被阻塞。

備註

長時間維持讀取鎖定或寫入鎖定會讓其他執行緒無法負荷。 為了達到最佳效能,可以考慮重新架構你的應用程式,以縮短寫入時間。

執行緒可以同時持有讀取鎖或寫入鎖,但不能同時持有兩者。 你不必釋放讀取鎖來取得寫入鎖,而是可以使用 UpgradeToWriterLockDowngradeFromWriterLock

遞迴鎖請求會增加鎖的鎖數量。

讀者與作者會分開排隊。 當執行緒釋放寫入鎖時,所有當時在讀取隊列中等待的執行緒都會獲得讀取鎖定;當所有讀取鎖定都被釋放後,下一個等待在寫入佇列中的執行緒(如有的話)會被授予寫入鎖定,依此類推。 換句話說,就是 ReaderWriterLock 在一群讀者和一位作家之間交替閱讀。

當寫入者隊列中的執行緒正在等待啟用的讀取鎖定釋放時,請求新讀取鎖的執行緒會累積在讀取者佇列中。 他們的請求不會被批准,儘管他們可能與現有的讀者鎖定持有者共享並行存取權限;這有助於保護寫手免於被讀者無限期封鎖。

大多數取得超 ReaderWriterLock 時值的鎖定方法都接受。 使用超時來避免申請陷入僵局。 例如,執行緒可能先取得一個資源的寫入鎖定,然後再請求第二個資源的讀取鎖定;同時,另一個執行緒可能會取得第二個資源的寫入鎖定,並請求第一個資源的讀取鎖定。 除非使用超時,執行緒會死結。

如果逾時間隔結束且鎖請求未被授予,該方法會透過拋出 ApplicationException。 執行緒可以捕捉此例外並決定下一步該採取的行動。

逾時以毫秒表示。 若使用 a System.TimeSpan 指定逾時,所使用的值即為 所代表 TimeSpan的整毫秒數。 下表顯示有效的逾時值(以毫秒為單位)。

Value Description
-1 線會等到鎖被取得為止,不論花多久時間。 對於指定整數超時的方法,可以使用常數 Infinite
0 線程不會等待取得鎖。 若無法立即取得鎖,則返回該方法。
>0 等待的時間是毫秒數。

除了 -1 外,不允許負的逾時值。 如果你指定非 -1 的負整數,則會使用逾時值為零。 (也就是說,若無法立即取得鎖定,方法會不等待返回。)若指定 a TimeSpan ,代表除 -1 外的負數毫秒, ArgumentOutOfRangeException 則會拋出。

建構函式

名稱 Description
ReaderWriterLock()

初始化 ReaderWriterLock 類別的新執行個體。

屬性

名稱 Description
IsReaderLockHeld

會取得一個值,表示目前執行緒是否持有讀取鎖。

IsWriterLockHeld

會取得一個值,表示目前執行緒是否持有寫入鎖。

WriterSeqNum

會取得目前的序號。

方法

名稱 Description
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 逾時值。

適用於

執行緒安全性

此類型是安全線程。

另請參閱