Monitor

更新 : 2008 年 7 月

Monitor オブジェクトは、Monitor.EnterMonitor.TryEnterMonitor.Exit の各メソッドを使用して特定のオブジェクトに対するロックを取得および解放することによって、コード領域へのアクセスを同期する機能を提供します。コード領域に対するロックを取得すると、Monitor.WaitMonitor.PulseMonitor.PulseAll の各メソッドが使用できます。Wait は、ロックが保持されている場合はそれを解放し、通知を受けるまで待機します。Wait は、通知を受けると、ロックを再び返したり、取得したりします。PulsePulseAll は、どちらも待機キュー内の次のスレッドに処理を行うよう通知します。

Visual Basic の SyncLock ステートメントと C# の lock ステートメントは、Monitor.Enter を使用してロックと Monitor.Exit を取得し、ロックを解放します。言語ステートメントを使用する利点は、Try ステートメントに lock ブロックまたは SyncLock ブロックのすべての処理が含まれていることです。Try ステートメントには、対応する Finally ブロックがあり、これによって確実にロックが解放されます。

Monitor は、値型ではなくオブジェクト (つまり参照型) をロックします。EnterExit には値型を渡せますが、呼び出しごとに個別にボックス化変換されます。呼び出しごとに別のオブジェクトが作成されるので、Enter がブロックを行うことはなく、それによって保護されていると推定されるコードは実際には同期されません。また、Exit に渡されるオブジェクトは Enter に渡されるオブジェクトとは異なるので、Monitor は "オブジェクト同期メソッドは、コードの非同期ブロックから呼び出されました。" というメッセージと共に SynchronizationLockException をスローします。このプロセスを説明する例を次に示します。

Private x As Integer
' The next line creates a generic object containing 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 following line creates another object containing 
    ' the value of x, and throws SynchronizationLockException
    ' because the two objects do not match.
    Monitor.Exit(x)
End Try
private int x;
// The next line creates a generic object containing 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 following line creates another object containing 
    // the value of x, and throws SynchronizationLockException
    // because the two objects do not match.
    Monitor.Exit(x);
}

次の例に示すように、EnterExit を呼び出す前に値型の変数をボックス化変換し、ボックス化変換された同じオブジェクトを両方のメソッドに渡すことができますが、そうする利点はありません。変数への変更はボックス化変換されたコピーには反映されず、ボックス化変換されたコピーの値を変更する方法もありません。

Private o As Object = x
private Object o = x;

Monitor オブジェクトと WaitHandle オブジェクトの使用方法の相違に注意する必要があります。Monitor オブジェクトは、完全に移植性のある純粋なマネージ オブジェクトであり、オペレーティング システム リソースの利用から考えると、より効率的な場合があります。WaitHandle オブジェクトは、オペレーティング システムの待機可能オブジェクトを表し、マネージ コードとアンマネージ コードを同期する場合に便利です。また、このオブジェクトは、一度に多くのオブジェクトで待機する機能などの高度なオペレーティング システム機能を提供します。

lock と SyncLock のコンパイラ ステートメントを使用して実装された Monitor クラス、Interlocked クラス、および AutoResetEvent クラスを組み合わせて使用するコード例を次に示します。

Imports System
Imports System.Threading
Imports Microsoft.VisualBasic

' 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 'Access
End Class 'SyncResource

' 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 'Access
End Class 'UnSyncResource

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()
   Private Shared threadNum As Int32
   Public Shared Sub Main()
      
      For threadNum = 0 To 4
         ThreadPool.QueueUserWorkItem(AddressOf SyncUpdateResource, threadNum)
      Next threadNum
      
      ' Wait until this WaitHandle is signaled.
      asyncOpsAreDone.WaitOne()
      Console.WriteLine(ControlChars.Tab + ControlChars.Lf + "All synchronized operations have completed." + ControlChars.Lf)
      
      ' Reset the thread count for unsynchronized calls.
      numAsyncOps = 5
      
      For threadNum = 0 To 4
         ThreadPool.QueueUserWorkItem(AddressOf UnSyncUpdateResource, threadNum)
      Next threadNum
      
      ' Wait until this WaitHandle is signaled.
      asyncOpsAreDone.WaitOne()
      Console.WriteLine(ControlChars.Tab + ControlChars.Cr + "All unsynchronized thread operations have completed.")
   End Sub 'Main
   
   
   
   ' 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
         asyncOpsAreDone.Set() 
         ' Announce to Main that in fact all thread calls are done.
      End If
   End Sub 'SyncUpdateResource
    
   ' 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
         asyncOpsAreDone.Set() 
         ' Announce to Main that in fact all thread calls are done.
      End If
   End Sub 'UnSyncUpdateResource 
End Class 'App
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)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }

   // 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)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }
}

参照

参照

Monitor

その他の技術情報

スレッド処理オブジェクトと機能

履歴の変更

日付

履歴

理由

2008 年 7 月

SyncLock ステートメントと lock ステートメントで Monitor.Enter および Exit が使用されることを明記

カスタマ フィードバック