MSDN Magazine Febrary 2005の記事を検証してみました。
http://msdn.microsoft.com/msdnmag/issues/05/02/BasicInstincts/default.aspx
といっても、メインのテーマではなくて、まずはMonitorクラスとSyncLockとの類似点(あるいは相違点)の確認。
#言うまでもなく、SyncLockはVisual Basic .NETのキーワード。C#ではlockです。
以下のような簡単なコードを書いてみました。
Pointを表す単純なクラスをMonitorを使う方法とSyncLockを使う方法とで比較するものです。
----------------------------------------------------------------
Imports System.Threading
Public Class PointMonitor
Private x As Integer
Private y As Integer
Sub New(ByVal x As Integer, ByVal y As Integer)
Me.x = x
Me.y = y
End Sub
Sub GetPointPosition(ByRef x As Integer, ByRef y As Integer)
Monitor.Enter(Me)
Try
x = Me.x
y = Me.y
Finally
Monitor.Exit(Me)
End Try
End Sub
Sub SetPointPosition(ByVal x As Integer, ByVal y As Integer)
Monitor.Enter(Me)
Try
Me.x = x
Me.y = y
Finally
Monitor.Exit(Me)
End Try
End Sub
End Class
Public Class PointLock
Private x As Integer
Private y As Integer
Sub New(ByVal x As Integer, ByVal y As Integer)
Me.x = x
Me.y = y
End Sub
Sub GetPointPosition(ByRef x As Integer, ByRef y As Integer)
SyncLock Me
x = Me.x
y = Me.y
End SyncLock
End Sub
Sub SetPointPosition(ByVal x As Integer, ByVal y As Integer)
SyncLock Me
Me.x = x
Me.y = y
End SyncLock
End Sub
End Class
----------------------------------------------------------------
これをビルド(Release Build)して、ディスアセンブラ(Ildasm.exe)を使って、実際にどのようなMSILコードが生成されたのか見てみました。
------ PointMonitorクラスのGetPointPositionメソッド ------
.method public instance void GetPointPosition(int32& x, int32& y) cil managed
{
// コード サイズ 32 (0x20)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call void [mscorlib]System.Threading.Monitor::Enter(object)
.try
{
IL_0006: ldarg.1
IL_0007: ldarg.0
IL_0008: ldfld int32 LockTest.PointMonitor::x
IL_000d: stind.i4
IL_000e: ldarg.2
IL_000f: ldarg.0
IL_0010: ldfld int32 LockTest.PointMonitor::y
IL_0015: stind.i4
IL_0016: leave.s IL_001f
} // end .try
finally
{
IL_0018: ldarg.0
IL_0019: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_001e: endfinally
} // end handler
IL_001f: ret
} // end of method PointMonitor::GetPointPosition
----------------------------------------------------------
------ PointLockクラスのGetPointPositionメソッド ------
.method public instance void GetPointPosition(int32& x, int32& y) cil managed
{
// コード サイズ 34 (0x22)
.maxstack 2
.locals init (class LockTest.PointLock V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: call void [mscorlib]System.Threading.Monitor::Enter(object)
.try
{
IL_0008: ldarg.1
IL_0009: ldarg.0
IL_000a: ldfld int32 LockTest.PointLock::x
IL_000f: stind.i4
IL_0010: ldarg.2
IL_0011: ldarg.0
IL_0012: ldfld int32 LockTest.PointLock::y
IL_0017: stind.i4
IL_0018: leave.s IL_0021
} // end .try
finally
{
IL_001a: ldloc.0
IL_001b: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0020: endfinally
} // end handler
IL_0021: ret
} // end of method PointLock::GetPointPosition
-------------------------------------------------------
Monitorを使う方がわずかにコードサイズは小さいようですが、ほぼ違いはないと言っていいでしょう。結局SyncLockの方もコンパイルするとMonitor.Enterが使われていますし。
ということでSyncLockはMonitorを使ってロックする方法に対する簡単な記述を提供してくれるものだということがわかるわけです。
TryだのFinallyだの書かなくていいぶん、コードがシンプルで見やすくなりますね。
とすると、Monitor.Enterを使うのは、言語仕様としてSyncLock(やC#のlock)に相当するキーワードを持っていない言語製品の場合に限られると思っていいかもしれません。あとは、コードでSyncLockを使う場合、頭の中でほぼ等価なMonitor.Enterと置き換えてみて、ロジックの確認をしてみるのはいいことでしょうね。
ただし、SyncLockでは足りないこともあって、それはアクセスの競合が発生した場合のタイムアウトが必要な場合。この時はMonitor.TryEnterメソッドを使うことになります。
上記の記事はなかなか興味深い内容になっています。一読をお勧めします。