Monitor
Monitor オブジェクトは、Monitor.Enter、Monitor.TryEnter、および Monitor.Exit の各メソッドを使用して特定のオブジェクトに対するロックを取得および解放することによって、コード領域へのアクセスを同期する機能を提供します。 コード領域に対するロックを取得すると、Monitor.Wait、Monitor.Pulse、および Monitor.PulseAll の各メソッドが使用できます。 Wait は、ロックが保持されている場合はそれを解放し、通知を受けるまで待機します。 Wait は、通知を受けると、制御を返して再びロックを取得します。 Pulse と PulseAll は、どちらも待機キュー内の次のスレッドに処理を行うよう通知します。
Visual Basic の SyncLock ステートメントと C# の lock ステートメントは、Monitor.Enter を使用してロックと Monitor.Exit を取得し、ロックを解放します。 言語ステートメントを使用する利点は、Try ステートメントに lock ブロックまたは SyncLock ブロックのすべての処理が含まれていることです。 Try ステートメントには、対応する Finally ブロックがあり、これによって確実にロックが解放されます。
Monitor は、値型ではなくオブジェクト (つまり参照型) をロックします。 Enter と Exit には値型を渡せますが、呼び出しごとに個別にボックス化変換されます。 呼び出しごとに別のオブジェクトが作成されるので、Enter がブロックを行うことはなく、それによって保護されていると推定されるコードは実際には同期されません。 また、Exit に渡されるオブジェクトは Enter に渡されるオブジェクトとは異なるので、Monitor は "オブジェクト同期メソッドは、コードの非同期ブロックから呼び出されました。" というメッセージと共に SynchronizationLockException をスローします。 このプロセスを説明する例を次に示します。
Try
Dim x As Integer = 1
' The call to Enter() creates a generic synchronizing object for the value
' of x each time the code is executed, so that Enter never blocks.
Monitor.Enter(x)
Try
' Code that needs to be protected by the monitor.
Finally
' Always use Finally to ensure that you exit the Monitor.
' The call to Exit() will FAIL!!!
' The synchronizing object created for x in Exit() will be different
' than the object used in Enter(). SynchronizationLockException
' will be thrown.
Monitor.Exit(x)
End Try
Catch SyncEx As SynchronizationLockException
Console.WriteLine("A SynchronizationLockException occurred. Message:")
Console.WriteLine(SyncEx.Message)
End Try
try
{
int x = 1;
// The call to Enter() creates a generic synchronizing object for the value
// of x each time the code is executed, so that Enter never blocks.
Monitor.Enter(x);
try
{
// Code that needs to be protected by the monitor.
}
finally
{
// Always use Finally to ensure that you exit the Monitor.
// The call to Exit() will FAIL!!!
// The synchronizing object created for x in Exit() will be different
// than the object used in Enter(). SynchronizationLockException
// will be thrown.
Monitor.Exit(x);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine("A SynchronizationLockException occurred. Message:");
Console.WriteLine(SyncEx.Message);
}
try
{
int x = 1;
// The call to Enter() creates a generic synchronizing object for the value
// of x each time the code is executed, so that Enter never blocks.
Monitor::Enter(x);
try
{
// Code that needs to be protected by the monitor.
}
finally
{
// Always use Finally to ensure that you exit the Monitor.
// The call to Exit() will FAIL!!!
// The synchronizing object created for x in Exit() will be different
// than the object used in Enter(). SynchronizationLockException
// will be thrown.
Monitor::Exit(x);
}
}
catch (SynchronizationLockException^ SyncEx)
{
Console::WriteLine("A SynchronizationLockException occurred. Message:");
Console::WriteLine(SyncEx->Message);
}
次の例に示すように、Enter と Exit を呼び出す前に値型の変数をボックス化変換し、ボックス化変換された同じオブジェクトを両方のメソッドに渡すことができますが、そうする利点はありません。 変数への変更はボックス化変換されたコピーには反映されず、ボックス化変換されたコピーの値を変更する方法もありません。
Dim x As Integer = 1
Dim o As object = x
Monitor.Enter(o)
Try
' Code that needs to be protected by the monitor.
Finally
' Always use Finally to ensure that you exit the Monitor.
Monitor.Exit(o)
End Try
int x = 1;
object o = x;
Monitor.Enter(o);
try
{
// Code that needs to be protected by the monitor.
}
finally
{
// Always use Finally to ensure that you exit the Monitor.
Monitor.Exit(o);
}
int x = 1;
Object^ o = x;
Monitor::Enter(o);
try
{
// Code that needs to be protected by the monitor.
}
finally
{
// Always use Finally to ensure that you exit the Monitor.
Monitor::Exit(o);
}
Monitor オブジェクトと WaitHandle オブジェクトの使用方法の相違に注意する必要があります。 Monitor オブジェクトは、完全に移植性のある純粋なマネージ オブジェクトであり、オペレーティング システム リソースの利用から考えると、より効率的な場合があります。 WaitHandle オブジェクトは、オペレーティング システムの待機可能オブジェクトを表し、マネージ コードとアンマネージ コードを同期する場合に便利です。また、このオブジェクトは、一度に多くのオブジェクトで待機する機能などの高度なオペレーティング システム機能を提供します。
lock と SyncLock のコンパイラ ステートメントを使用して実装された Monitor クラス、Interlocked クラス、および AutoResetEvent クラスを組み合わせて使用するコード例を次に示します。
Imports System
Imports System.Threading
' Note: The class whose internal public member is the synchronizing
' method is not public; none of the client code takes a lock on the
' Resource object.The member of the nonpublic class takes the lock on
' itself. Written this way, malicious code cannot take a lock on
' a public object.
Class SyncResource
Public Sub Access(threadNum As Int32)
' Uses Monitor class to enforce synchronization.
SyncLock Me
' Synchronized: Despite the next conditional, each thread
' waits on its predecessor.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum)
Thread.Sleep(200)
Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum)
End SyncLock
End Sub
End Class
' Without the lock, the method is called in the order in which threads reach it.
Class UnSyncResource
Public Sub Access(threadNum As Int32)
' Does not use Monitor class to enforce synchronization.
' The next call throws the thread order.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum)
Thread.Sleep(200)
Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum)
End Sub
End Class
Public Class App
Private Shared numAsyncOps As Int32 = 5
Private Shared asyncOpsAreDone As New AutoResetEvent(false)
Private Shared SyncRes As New SyncResource()
Private Shared UnSyncRes As New UnSyncResource()
Public Shared Sub Main()
For threadNum As Int32 = 0 To 4
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf SyncUpdateResource), threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Console.WriteLine(vbTab + vbNewLine + "All synchronized operations have completed." + vbTab + vbNewLine)
' Reset the thread count for unsynchronized calls.
numAsyncOps = 5
For threadNum As Int32 = 0 To 4
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf UnSyncUpdateResource), threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Console.WriteLine("\t\nAll unsynchronized thread operations have completed.")
End Sub
' The callback method's signature MUST match that of a
' System.Threading.TimerCallback delegate (it takes an Object
' parameter and returns void).
Shared Sub SyncUpdateResource(state As Object)
' This calls the internal synchronized method, passing
' a thread number.
SyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, however; you cannot know which thread
' will access the value **before** another thread's incremented
' value has been stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
' Announce to Main that in fact all thread calls are done.
asyncOpsAreDone.Set()
End If
End Sub
' The callback method's signature MUST match that of a
' System.Threading.TimerCallback delegate (it takes an Object
' parameter and returns void).
Shared Sub UnSyncUpdateResource(state As Object)
' This calls the unsynchronized method, passing a thread number.
UnSyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, however; you cannot know which thread
' will access the value **before** another thread's incremented
' value has been stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
' Announce to Main that in fact all thread calls are done.
asyncOpsAreDone.Set()
End If
End Sub
End Class
using System;
using System.Threading;
// Note: The class whose internal public member is the synchronizing
// method is not public; none of the client code takes a lock on the
// Resource object.The member of the nonpublic class takes the lock on
// itself. Written this way, malicious code cannot take a lock on
// a public object.
class SyncResource
{
public void Access(Int32 threadNum)
{
// Uses Monitor class to enforce synchronization.
lock (this)
{
// Synchronized: Despite the next conditional, each thread
// waits on its predecessor.
if (threadNum % 2 == 0)
{
Thread.Sleep(2000);
}
Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
}
}
}
// Without the lock, the method is called in the order in which threads reach it.
class UnSyncResource
{
public void Access(Int32 threadNum)
{
// Does not use Monitor class to enforce synchronization.
// The next call throws the thread order.
if (threadNum % 2 == 0)
{
Thread.Sleep(2000);
}
Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
}
}
public class App
{
static Int32 numAsyncOps = 5;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
static SyncResource SyncRes = new SyncResource();
static UnSyncResource UnSyncRes = new UnSyncResource();
public static void Main()
{
for (Int32 threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("\t\nAll synchronized operations have completed.\t\n");
// Reset the thread count for unsynchronized calls.
numAsyncOps = 5;
for (Int32 threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("\t\nAll unsynchronized thread operations have completed.");
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void SyncUpdateResource(Object state)
{
// This calls the internal synchronized method, passing
// a thread number.
SyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
{
// Announce to Main that in fact all thread calls are done.
asyncOpsAreDone.Set();
}
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void UnSyncUpdateResource(Object state)
{
// This calls the unsynchronized method, passing a thread number.
UnSyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
{
// Announce to Main that in fact all thread calls are done.
asyncOpsAreDone.Set();
}
}
}
#using <System.dll>
using namespace System;
using namespace System::Threading;
// Note: The class whose internal public member is the synchronizing
// method is not public; none of the client code takes a lock on the
// Resource object.The member of the nonpublic class takes the lock on
// itself. Written this way, malicious code cannot take a lock on
// a public object.
ref class SyncResource
{
public:
void Access(Int32 threadNum)
{
// Uses Monitor class to enforce synchronization.
Monitor::Enter(this);
try
{
// Synchronized: Despite the next conditional, each thread
// waits on its predecessor.
if (threadNum % 2 == 0)
{
Thread::Sleep(2000);
}
Console::WriteLine("Start Synched Resource access (Thread={0})", threadNum);
Thread::Sleep(200);
Console::WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
}
finally
{
Monitor::Exit(this);
}
}
};
// Without the lock, the method is called in the order in which threads reach it.
ref class UnSyncResource
{
public:
void Access(Int32 threadNum)
{
// Does not use Monitor class to enforce synchronization.
// The next call throws the thread order.
if (threadNum % 2 == 0)
{
Thread::Sleep(2000);
}
Console::WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
Thread::Sleep(200);
Console::WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
}
};
public ref class App
{
private:
static Int32 numAsyncOps = 5;
static AutoResetEvent^ asyncOpsAreDone = gcnew AutoResetEvent(false);
static SyncResource^ SyncRes = gcnew SyncResource();
static UnSyncResource^ UnSyncRes = gcnew UnSyncResource();
public:
static void Main()
{
for (Int32 threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool::QueueUserWorkItem(gcnew WaitCallback(SyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone->WaitOne();
Console::WriteLine("\t\nAll synchronized operations have completed.\t\n");
// Reset the thread count for unsynchronized calls.
numAsyncOps = 5;
for (Int32 threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool::QueueUserWorkItem(gcnew WaitCallback(UnSyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone->WaitOne();
Console::WriteLine("\t\nAll unsynchronized thread operations have completed.");
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void SyncUpdateResource(Object^ state)
{
// This calls the internal synchronized method, passing
// a thread number.
SyncRes->Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked::Decrement(numAsyncOps) == 0)
{
// Announce to Main that in fact all thread calls are done.
asyncOpsAreDone->Set();
}
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void UnSyncUpdateResource(Object^ state)
{
// This calls the unsynchronized method, passing a thread number.
UnSyncRes->Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked::Decrement(numAsyncOps) == 0)
{
// Announce to Main that in fact all thread calls are done.
asyncOpsAreDone->Set();
}
}
};
int main()
{
App::Main();
}