ReaderWriterLock クラス
単一ライタと複数リーダーをサポートするロックを定義します。
この型のすべてのメンバの一覧については、ReaderWriterLock メンバ を参照してください。
System.Object
System.Threading.ReaderWriterLock
NotInheritable Public Class ReaderWriterLock
[C#]
public sealed class ReaderWriterLock
[C++]
public __gc __sealed class ReaderWriterLock
[JScript]
public class ReaderWriterLock
スレッドセーフ
この型は、マルチスレッド操作に対して安全です。
解説
ReaderWriterLock は、リソースへのアクセスを同期するときに使用します。このクラスでは、複数のスレッドによる同時読み取りアクセスまたは 1 つのスレッドによる書き込みアクセスのいずれかが常に許可されます。リソースの変更が頻繁に発生しない場合は、 Monitor のような "一度に 1 つ" 方式の単純なロックより、 ReaderWriterLock の方が優れたスループットを得ることができます。
ReaderWriterLock は、ほとんどのアクセスが読み取りアクセスで、書き込みアクセスはほとんど発生せず、短時間で終了するような場合に適しています。複数リーダーと単一ライタは交互に処理されるため、リーダーとライタの両方が長時間にわたってブロックされることはありません。
メモ リーダー ロックまたはライタ ロックを長時間にわたって保持すると、他のスレッドが処理を続行できなくなります。最適なパフォーマンスを得るためには、書き込みの時間が短くなるようにアプリケーションを設計してください。
1 つのスレッドはリーダー ロックまたはライタ ロックを保持できますが、一度に両方を取得することはできません。ライタ ロックを取得するためにリーダー ロックを解放する必要がある場合は、代わりに UpgradeToWriterLock と DowngradeFromWriterLock を使用することもできます。
再帰的にロックを要求すると、ロックのロック カウントがインクリメントされます。
リーダーとライタのキューは別々に存在します。ライタ ロックを保持していたスレッドがロックを解放すると、そのときリーダー キューで待機していたすべてのスレッドに対してリーダー ロックが与えられます。これによりリーダー ロックを保持していたすべてのスレッドがロックを解放すると、今度はライタ キューで待機していた次のスレッドにライタ ロックが与えられます。これが繰り返されます。つまり、 ReaderWriterLock は、複数のリーダーと 1 つのライタを交互に処理します。
現在のリーダー ロックの解放を待機しているスレッドがライタ キューにある場合、新しくリーダー ロックを要求するスレッドはリーダー キューに格納されます。この場合、既にリーダー ロックを保持しているスレッドと同時アクセスを共有できたとしても、この新しいリーダー ロックの要求はすぐには処理されません。これは、リーダーによる無制限のブロックからライタを保護するためのしくみです。
ReaderWriterLock のロック取得に関するメソッドのほとんどは、タイムアウト値を受け取ることができます。タイムアウト値を使用すると、アプリケーションでデッドロックが発生することを防ぐことができます。たとえば、あるスレッドがあるリソースに対してライタ ロックを取得し、次に 2 番目のリソースに対してリーダー ロックを要求、同時に別のスレッドがこの 2 番目のリソースに対してライタ ロックを取得して、1 番目のリソースに対してリーダー ロックを要求したとします。このような場合は、タイムアウト値を使用しないと、2 つのスレッドでデッドロックが発生します。
タイムアウト時間が経過して、ロック要求が許可されなかった場合、メソッドは ApplicationException をスローして制御を呼び出し元のスレッドに戻します。スレッドはこの例外をキャッチして、次に実行するアクションを指定できます。
タイムアウト値はミリ秒単位で表されます。 System.TimeSpan を使用してタイムアウト値を指定する場合、使用される値は、 TimeSpan が表すミリ秒数の合計値となります。有効なタイムアウト値 (ミリ秒) を次の表示に示します。
値 | 説明 |
---|---|
-1 | Infinite. |
0 | タイムアウトなし。 |
> 0 | ミリ秒数単位の待機時間。 |
-1 を例外として、タイムアウト値には負の値を指定できません。-1 以外の負の整数値を指定した場合、タイムアウト値には 0 (タイムアウトなし) が適用されます。-1 以外の負のミリ秒値を表す TimeSpan を指定した場合は、 ArgumentOutOfRangeException がスローされます。
使用例
' This example shows a ReaderWriterLock protecting a shared
' resource that is read concurrently and written exclusively
' by multiple threads.
' The complete code is located in the ReaderWriterLock
' class topic.
Imports System
Imports System.Threading
Imports Microsoft.VisualBasic
Public Class Test
' Declaring the ReaderWriterLock at the class level
' makes it visible to all threads.
Private Shared rwl As New ReaderWriterLock()
' For this example, the shared resource protected by the
' ReaderWriterLock is just an integer.
Private Shared resource As Integer = 0
Const numThreads As Integer = 26
Private Shared running As Boolean = True
Private Shared rnd As New Random()
' Statistics.
Private Shared readerTimeouts As Integer = 0
Private Shared writerTimeouts As Integer = 0
Private Shared reads As Integer = 0
Private Shared writes As Integer = 0
Public Shared Sub Main(args() As String)
' Start a series of threads. Each thread randomly
' performs reads and writes on the shared resource.
Dim t(numThreads) 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 i
' Tell the threads to shut down, then wait until they all
' finish.
running = False
For i = 0 To numThreads - 1
t(i).Join()
Next i
' Display statistics.
Console.WriteLine(vbCrLf & "{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.", reads, writes, readerTimeouts, writerTimeouts)
Console.WriteLine("Press ENTER to exit.")
Console.ReadLine()
End Sub 'Main
Shared Sub ThreadProc()
' As long as a thread runs, it randomly selects
' various ways to read and write from the shared
' resource. Each of the methods demonstrates one
' or more features of ReaderWriterLock.
While running
Dim action As Double = rnd.NextDouble()
If action < 0.8 Then
ReadFromResource(10)
ElseIf action < 0.81 Then
ReleaseRestore(50)
ElseIf action < 0.9 Then
UpgradeDowngrade(100)
Else
WriteToResource(100)
End If
End While
End Sub 'ThreadProc
' Shows how to request and release a reader lock, and
' how to handle time-outs.
Shared Sub ReadFromResource(timeOut As Integer)
Try
rwl.AcquireReaderLock(timeOut)
Try
' It is 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 'ReadFromResource
' Shows how to request and release the writer lock, and
' how to handle time-outs.
Shared Sub WriteToResource(timeOut As Integer)
Try
rwl.AcquireWriterLock(timeOut)
Try
' It is 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 'WriteToResource
' Shows how to request a reader lock, upgrade the
' reader lock to the writer lock, and downgrade to a
' reader lock again.
Shared Sub UpgradeDowngrade(timeOut As Integer)
Try
rwl.AcquireReaderLock(timeOut)
Try
' It is safe for this thread to read from
' the shared resource.
Display("reads resource value " & resource)
Interlocked.Increment(reads)
' If it is necessary to write to the resource,
' you must either release the reader lock and
' then request the writer lock, or upgrade the
' reader lock. Note that 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 is 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
' When the lock has been downgraded, it is
' 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 'UpgradeDowngrade
' Shows how to release all locks and later restore
' the lock state. Shows how to use sequence numbers
' to determine whether another thread has obtained
' a writer lock since this thread last accessed the
' resource.
Shared Sub ReleaseRestore(timeOut As Integer)
Dim lastWriter As Integer
Try
rwl.AcquireReaderLock(timeOut)
Try
' It is safe for this thread to read from
' the shared resource. Cache the value. (You
' might do this if reading the resource is
' an expensive operation.)
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 (up to a
' quarter of a second), and then restore
' the previous state of the lock. Note that
' there is no time-out on the Restore method.
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 'ReleaseRestore
' Helper method briefly displays the most recent
' thread action. Comment out calls to Display to
' get a better idea of throughput.
Shared Sub Display(msg As String)
Console.Write("Thread {0} {1}. " & vbCr, Thread.CurrentThread.Name, msg)
End Sub 'Display
End Class 'Test
[C#]
// This example shows a ReaderWriterLock protecting a shared
// resource that is read concurrently and written exclusively
// by multiple threads.
// The complete code is located in the ReaderWriterLock
// class topic.
using System;
using System.Threading;
public class Test
{
// Declaring the ReaderWriterLock at the class level
// makes it visible to all threads.
static ReaderWriterLock rwl = new ReaderWriterLock();
// For this example, the shared resource protected by the
// ReaderWriterLock is just an integer.
static int resource = 0;
const int numThreads = 26;
static bool running = true;
static Random rnd = new Random();
// Statistics.
static int readerTimeouts = 0;
static int writerTimeouts = 0;
static int reads = 0;
static int writes = 0;
public static void Main(string[] args)
{
// Start a series of threads. Each thread randomly
// performs reads and writes on 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(Convert.ToChar(i + 65), 1);
t[i].Start();
if (i > 10)
Thread.Sleep(300);
}
// Tell the threads to shut down, then wait until they all
// finish.
running = false;
for (int i = 0; i < numThreads; i++)
{
t[i].Join();
}
// Display statistics.
Console.WriteLine("\r\n{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
reads, writes, readerTimeouts, writerTimeouts);
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
static void ThreadProc()
{
// As long as a thread runs, it randomly selects
// various ways to read and write from the shared
// resource. Each of the methods demonstrates one
// or more features of ReaderWriterLock.
while (running)
{
double action = rnd.NextDouble();
if (action < .8)
ReadFromResource(10);
else if (action < .81)
ReleaseRestore(50);
else if (action < .90)
UpgradeDowngrade(100);
else
WriteToResource(100);
}
}
// Shows how to request and release a reader lock, and
// how to 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);
}
}
// Shows how to request and release the writer lock, and
// how to handle time-outs.
static void WriteToResource(int timeOut)
{
try
{
rwl.AcquireWriterLock(timeOut);
try
{
// It is 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.ReleaseWriterLock();
}
}
catch (ApplicationException)
{
// The writer lock request timed out.
Interlocked.Increment(ref writerTimeouts);
}
}
// Shows how to request a reader lock, upgrade the
// reader lock to the writer lock, and downgrade to a
// reader lock again.
static void UpgradeDowngrade(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);
// If it is necessary to write to the resource,
// you must either release the reader lock and
// then request the writer lock, or upgrade the
// reader lock. Note that 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 is 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);
}
// When the lock has been downgraded, it is
// 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);
}
}
// Shows how to release all locks and later restore
// the lock state. Shows how to use sequence numbers
// to determine whether another thread has obtained
// a writer lock since this thread last accessed the
// resource.
static void ReleaseRestore(int timeOut)
{
int lastWriter;
try
{
rwl.AcquireReaderLock(timeOut);
try
{
// It is safe for this thread to read from
// the shared resource. Cache the value. (You
// might do this if reading the resource is
// an expensive operation.)
int resourceValue = resource;
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 (up to a
// quarter of a second), and then restore
// the previous state of the lock. Note that
// there is no time-out on the Restore method.
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. Comment out calls to Display to
// get a better idea of throughput.
static void Display(string msg)
{
Console.Write("Thread {0} {1}. \r", Thread.CurrentThread.Name, msg);
}
}
[C++]
// This example shows a ReaderWriterLock protecting a shared
// resource that is read concurrently and written exclusively
// by multiple threads.
// The complete code is located in the ReaderWriterLock
// class topic.
#using <mscorlib.dll>
using namespace System;
using namespace System::Threading;
public __gc class Test
{
public:
// Declaring the ReaderWriterLock at the class level
// makes it visible to all threads.
static ReaderWriterLock* rwl = new ReaderWriterLock();
// For this example, the shared resource protected by the
// ReaderWriterLock is just an integer.
static int resource = 0;
static const int numThreads = 26;
static bool running = true;
static Random* rnd = new Random();
// Statistics.
static int readerTimeouts = 0;
static int writerTimeouts = 0;
static int reads = 0;
static int writes = 0;
static void ThreadProc()
{
// As long as a thread runs, it randomly selects
// various ways to read and write from the shared
// resource. Each of the methods demonstrates one
// or more features of ReaderWriterLock.
while (running)
{
double action = rnd->NextDouble();
if (action < .8)
ReadFromResource(10);
else if (action < .81)
ReleaseRestore(50);
else if (action < .90)
UpgradeDowngrade(100);
else
WriteToResource(100);
}
}
// Shows how to request and release a reader lock, and
// how to 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(String::Format(S"reads resource value {0}", __box(resource)));
Interlocked::Increment(&reads);
}
__finally
{
// Ensure that the lock is released.
rwl->ReleaseReaderLock();
}
}
catch (ApplicationException*)
{
// The reader lock request timed out.
Interlocked::Increment(&readerTimeouts);
}
}
// Shows how to request and release the writer lock, and
// how to handle time-outs.
static void WriteToResource(int timeOut)
{
try
{
rwl->AcquireWriterLock(timeOut);
try
{
// It is safe for this thread to read or write
// from the shared resource.
resource = rnd->Next(500);
Display(String::Format(S"writes resource value {0}", __box(resource)));
Interlocked::Increment(&writes);
}
__finally
{
// Ensure that the lock is released.
rwl->ReleaseWriterLock();
}
}
catch (ApplicationException*)
{
// The writer lock request timed out.
Interlocked::Increment(&writerTimeouts);
}
}
// Shows how to request a reader lock, upgrade the
// reader lock to the writer lock, and downgrade to a
// reader lock again.
static void UpgradeDowngrade(int timeOut)
{
try
{
rwl->AcquireReaderLock(timeOut);
try
{
// It is safe for this thread to read from
// the shared resource.
Display(String::Format(S"reads resource value {0}", __box(resource)));
Interlocked::Increment(&reads);
// If it is necessary to write to the resource,
// you must either release the reader lock and
// then request the writer lock, or upgrade the
// reader lock. Note that 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 is safe for this thread to read or write
// from the shared resource.
resource = rnd->Next(500);
Display(String::Format(S"writes resource value {0}", __box(resource)));
Interlocked::Increment(&writes);
}
__finally
{
// Ensure that the lock is released.
rwl->DowngradeFromWriterLock(__box(lc));
}
}
catch (ApplicationException*)
{
// The upgrade request timed out.
Interlocked::Increment(&writerTimeouts);
}
// When the lock has been downgraded, it is
// still safe to read from the resource.
Display(String::Format(S"reads resource value {0}", __box(resource)));
Interlocked::Increment(&reads);
}
__finally
{
// Ensure that the lock is released.
rwl->ReleaseReaderLock();
}
}
catch (ApplicationException*)
{
// The reader lock request timed out.
Interlocked::Increment(&readerTimeouts);
}
}
// Shows how to release all locks and later restore
// the lock state. Shows how to use sequence numbers
// to determine whether another thread has obtained
// a writer lock since this thread last accessed the
// resource.
static void ReleaseRestore(int timeOut)
{
int lastWriter;
try
{
rwl->AcquireReaderLock(timeOut);
try
{
// It is safe for this thread to read from
// the shared resource. Cache the value. (You
// might do this if reading the resource is
// an expensive operation.)
int resourceValue = resource;
Display(String::Format(S"reads resource value {0}", __box(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.
LockCookie lc = rwl->ReleaseLock();
// Wait for a random interval (up to a
// quarter of a second), and then restore
// the previous state of the lock. Note that
// there is no time-->Item[Out] on* the Restore method.
Thread::Sleep(rnd->Next(250));
rwl->RestoreLock(__box(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(&reads);
Display(String::Format(S"resource has changed {0}", __box(resourceValue)));
}
else
{
Display(String::Format(S"resource has not changed {0}", __box(resourceValue)));
}
}
__finally
{
// Ensure that the lock is released.
rwl->ReleaseReaderLock();
}
}
catch (ApplicationException*)
{
// The reader lock request timed out.
Interlocked::Increment(&readerTimeouts);
}
}
// Helper method briefly displays the most recent
// thread action. Comment [Out] calls* to Display to
// get a better idea of throughput.
static void Display(String* msg)
{
Console::Write(S"Thread {0} {1}. \r", Thread::CurrentThread->Name, msg);
}
};
int main()
{
String* args[] = Environment::GetCommandLineArgs();
// Start a series of threads. Each thread randomly
// performs reads and writes on the shared resource.
Thread* t[] = new Thread*[Test::numThreads];
for (int i = 0; i < Test::numThreads; i++)
{
t->Item[i] = new Thread(new ThreadStart(0, Test::ThreadProc));
t[i]->Name = new String(Convert::ToChar(i + 65), 1);
t[i]->Start();
if (i > 10)
Thread::Sleep(300);
}
// Tell the threads to shut down, then wait until they all
// finish.
Test::running = false;
for (int i = 0; i < Test::numThreads; i++)
{
t[i]->Join();
}
// Display statistics.
Console::WriteLine(S"\r\n {0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
__box(Test::reads), __box(Test::writes), __box(Test::readerTimeouts), __box(Test::writerTimeouts));
Console::WriteLine(S"Press ENTER to exit.");
Console::ReadLine();
return 0;
}
[JScript] JScript のサンプルはありません。Visual Basic、C#、および C++ のサンプルを表示するには、このページの左上隅にある言語のフィルタ ボタン をクリックします。
必要条件
名前空間: System.Threading
プラットフォーム: Windows 98, Windows NT 4.0, Windows Millennium Edition, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 ファミリ
アセンブリ: Mscorlib (Mscorlib.dll 内)
参照
ReaderWriterLock メンバ | System.Threading 名前空間 | スレッド処理 | ReaderWriterLock