モニタとは
モニタ(Monitor)とは、任意のオブジェクトに対するアクセスを同期するための排他処理の概念の一つ。 クリティカルセクションに対する排他制御を行う目的で使用する。
モニタは、同期対象のオブジェクトに対するロックの取得と解放の二つの操作を提供する。 モニタは、同期対象のオブジェクトがロックされている間は、そのロックが解放されるまでは他のスレッドに対してオブジェクトへのアクセスをブロックする。 ロックは取得したスレッドが解放しなければならない。 この動作により、クリティカルセクションにおける排他処理を実現し、同期対象のオブジェクトに対する要求が競合しないように保護することができる。
.NET FrameworkにおけるMonitor
Monitorクラスは、インスタンスを生成することはできない。 同期対象のオブジェクト(同期オブジェクト)に対するロックの取得と解放は、MonitorクラスのstaticメソッドEnter()およびExit()を用いて行う。
C#のlock構文(VB.NETではSyncLock構文)は、コンパイル時にMonitorクラスを用いたコードに展開される。 すなわち、
lock (syncobj)
{
...
}
というlock構文を用いたコードは、
Monitor.Enter(syncobj);
try {
...
}
finally {
Monitor.Exit(syncobj);
}
というMonitorクラスを用いたコードとほぼ等価となる。
主なメソッド
- static void Enter(object)
- 任意の同期オブジェクトに対するロックを取得し、クリティカルセクションに入る。
- static void Exit(object)
- 任意の同期オブジェクトに対するロックを解放し、クリティカルセクションから出る。
主な呼び出し順序
- Monitor.Enter()
- Monitor.Exit()
使用例
Monitorを用いてクリティカルセクションに対する排他制御を行い、クリティカルセクションに対して複数のスレッドから同時にアクセスを試みる。
using System;
using System.Threading;
class Sample {
static object syncobj = null;
static void Main()
{
// 同期対象のオブジェクト
syncobj = new object();
// スレッドを5本生成
for (int i = 0; i < 5; i++) {
Thread t = new Thread(CriticalSection);
t.Start();
}
// 生成したスレッドの終了を待機する。
Thread.Sleep(750);
}
private static void CriticalSection()
{
Console.WriteLine("ThreadID#{0:X8}: Waiting", Thread.CurrentThread.ManagedThreadId);
Monitor.Enter(syncobj);
try {
Console.WriteLine("ThreadID#{0:X8}: Enter", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
finally {
Monitor.Exit(syncobj);
Console.WriteLine("ThreadID#{0:X8}: Exit", Thread.CurrentThread.ManagedThreadId);
}
}
}
Imports System
Imports System.Threading
Class Sample
Shared syncobj As Object = Nothing
Shared Sub Main()
' 同期対象のオブジェクト
syncobj = New Object()
' スレッドを5本生成
For i As Integer = 1 To 5
Dim t As New Thread(AddressOf CriticalSection)
t.Start()
Next
' 生成したスレッドの終了を待機する。
Thread.Sleep(750)
End Sub
Private Shared Sub CriticalSection()
Console.WriteLine("ThreadID#{0:X8}: Waiting", Thread.CurrentThread.ManagedThreadId)
Monitor.Enter(syncobj)
Try
Console.WriteLine("ThreadID#{0:X8}: Enter", Thread.CurrentThread.ManagedThreadId)
Thread.Sleep(100)
Finally
Monitor.Exit(syncobj)
Console.WriteLine("ThreadID#{0:X8}: Exit", Thread.CurrentThread.ManagedThreadId)
End Try
End Sub
End Class
実行結果
ThreadID#00000006: Waiting ThreadID#00000004: Waiting ThreadID#00000004: Enter ThreadID#00000007: Waiting ThreadID#00000003: Waiting ThreadID#00000005: Waiting ThreadID#00000004: Exit ThreadID#00000003: Enter ThreadID#00000003: Exit ThreadID#00000007: Enter ThreadID#00000007: Exit ThreadID#00000005: Enter ThreadID#00000005: Exit ThreadID#00000006: Enter ThreadID#00000006: Exit
上記例のCriticalSection()メソッドを次のように記述しても同様の結果となる。
lock構文を用いて記述した例
private void CriticalSection()
{
Console.WriteLine("ThreadID#{0:X8}: Waiting", Thread.CurrentThread.ManagedThreadId);
lock(syncobj)
{
Console.WriteLine("ThreadID#{0:X8}: Enter", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
Console.WriteLine("ThreadID#{0:X8}: Exit", Thread.CurrentThread.ManagedThreadId);
}
SyncLock構文を用いて記述した例
Private Shared Sub CriticalSection()
Console.WriteLine("ThreadID#{0:X8}: Waiting", Thread.CurrentThread.ManagedThreadId)
SyncLock syncobj
Console.WriteLine("ThreadID#{0:X8}: Enter", Thread.CurrentThread.ManagedThreadId)
Thread.Sleep(100)
End SyncLock
Console.WriteLine("ThreadID#{0:X8}: Exit", Thread.CurrentThread.ManagedThreadId)
End Sub