Monitor Monitor Monitor Monitor Class

定義

オブジェクトへのアクセスを同期する機構を提供します。Provides a mechanism that synchronizes access to objects.

public ref class Monitor abstract sealed
[System.Runtime.InteropServices.ComVisible(true)]
public static class Monitor
type Monitor = class
Public Class Monitor
継承
MonitorMonitorMonitorMonitor
属性

クラスを使用MonitorRandomて、クラスによって表される乱数ジェネレーターの1つのインスタンスへのアクセスを同期する例を次に示します。The following example uses the Monitor class to synchronize access to a single instance of a random number generator represented by the Random class. この例では、スレッドプールのスレッドで非同期的に実行される10個のタスクを作成します。The example creates ten tasks, each of which executes asynchronously on a thread pool thread. 各タスクは、1万の乱数を生成し、平均値を計算し、生成された乱数の合計数と合計を保持する2つのプロシージャレベル変数を更新します。Each task generates 10,000 random numbers, calculates their average, and updates two procedure-level variables that maintain a running total of the number of random numbers generated and their sum. すべてのタスクが実行された後、これらの2つの値を使用して全体的な平均が計算されます。After all tasks have executed, these two values are then used to calculate the overall mean.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      List<Task> tasks = new List<Task>();
      Random rnd = new Random();
      long total = 0;
      int n = 0;
      
      for (int taskCtr = 0; taskCtr < 10; taskCtr++)
         tasks.Add(Task.Run( () => {  int[] values = new int[10000];
                                      int taskTotal = 0;
                                      int taskN = 0;
                                      int ctr = 0;
                                      Monitor.Enter(rnd);
                                         // Generate 10,000 random integers
                                         for (ctr = 0; ctr < 10000; ctr++)
                                            values[ctr] = rnd.Next(0, 1001);
                                      Monitor.Exit(rnd);
                                      taskN = ctr;
                                      foreach (var value in values)
                                         taskTotal += value;

                                      Console.WriteLine("Mean for task {0,2}: {1:N2} (N={2:N0})",
                                                        Task.CurrentId, (taskTotal * 1.0)/taskN,
                                                        taskN);
                                      Interlocked.Add(ref n, taskN);
                                      Interlocked.Add(ref total, taskTotal);
                                    } ));
      try {
         Task.WaitAll(tasks.ToArray());
         Console.WriteLine("\nMean for all tasks: {0:N2} (N={1:N0})",
                           (total * 1.0)/n, n);

      }
      catch (AggregateException e) {
         foreach (var ie in e.InnerExceptions)
            Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message);
      }
   }
}
// The example displays output like the following:
//       Mean for task  1: 499.04 (N=10,000)
//       Mean for task  2: 500.42 (N=10,000)
//       Mean for task  3: 499.65 (N=10,000)
//       Mean for task  8: 502.59 (N=10,000)
//       Mean for task  5: 502.75 (N=10,000)
//       Mean for task  4: 494.88 (N=10,000)
//       Mean for task  7: 499.22 (N=10,000)
//       Mean for task 10: 496.45 (N=10,000)
//       Mean for task  6: 499.75 (N=10,000)
//       Mean for task  9: 502.79 (N=10,000)
//
//       Mean for all tasks: 499.75 (N=100,000)
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim tasks As New List(Of Task)()
      Dim rnd As New Random()
      Dim total As Long = 0
      Dim n As Integer = 0

      For taskCtr As Integer = 0 To 9
         tasks.Add(Task.Run( Sub()
                                Dim values(9999) As Integer
                                Dim taskTotal As Integer = 0
                                Dim taskN As Integer = 0
                                Dim ctr As Integer = 0
                                Monitor.Enter(rnd)
                                   ' Generate 10,000 random integers.
                                    For ctr = 0 To 9999
                                       values(ctr) = rnd.Next(0, 1001)
                                    Next
                                Monitor.Exit(rnd)
                                taskN = ctr
                                For Each value in values
                                   taskTotal += value
                                Next
                                    
                                Console.WriteLine("Mean for task {0,2}: {1:N2} (N={2:N0})",
                                                  Task.CurrentId, taskTotal/taskN,
                                                  taskN)
                                Interlocked.Add(n, taskN)
                                Interlocked.Add(total, taskTotal)
                             End Sub ))
      Next
      
      Try
         Task.WaitAll(tasks.ToArray())
         Console.WriteLine()
         Console.WriteLine("Mean for all tasks: {0:N2} (N={1:N0})",
                           (total * 1.0)/n, n)
      Catch e As AggregateException
         For Each ie In e.InnerExceptions
            Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message)
         Next
      End Try
   End Sub
End Module
' The example displays output like the following:
'       Mean for task  1: 499.04 (N=10,000)
'       Mean for task  2: 500.42 (N=10,000)
'       Mean for task  3: 499.65 (N=10,000)
'       Mean for task  8: 502.59 (N=10,000)
'       Mean for task  5: 502.75 (N=10,000)
'       Mean for task  4: 494.88 (N=10,000)
'       Mean for task  7: 499.22 (N=10,000)
'       Mean for task 10: 496.45 (N=10,000)
'       Mean for task  6: 499.75 (N=10,000)
'       Mean for task  9: 502.79 (N=10,000)
'
'       Mean for all tasks: 499.75 (N=100,000)

スレッドプールのスレッドで実行されている任意のタスクからアクセスできるため、変数totalnのアクセスとも同期される必要があります。Because they can be accessed from any task running on a thread pool thread, access to the variables total and n must also be synchronized. このInterlocked.Add目的では、メソッドが使用されます。The Interlocked.Add method is used for this purpose.

次のMonitor例でlockは、クラス (またはSyncLock言語AutoResetEventコンストラクトで実装)、 Interlockedクラス、およびクラスを組み合わせて使用する方法を示します。The following example demonstrates the combined use of the Monitor class (implemented with the lock or SyncLock language construct), the Interlocked class, and the AutoResetEvent class. 2 つの internal クラス (C# の場合) または Friend クラス (Visual Basic の場合)、SyncResourceUnSyncResource を定義します。これらはそれぞれ、リソースへの同期アクセスと非同期アクセスを提供します。It defines two internal (in C#) or Friend (in Visual Basic) classes, SyncResource and UnSyncResource, that respectively provide synchronized and unsynchronized access to a resource. 同期アクセスと非同期アクセスの違い (各メソッド呼び出しが迅速に完了する場合に違いが生じる可能性がある) を示すために、次の例では、メソッドにランダムな遅延を含めてあります。Thread.ManagedThreadId プロパティが偶数であるスレッドでは、メソッドが Thread.Sleep を呼び出して、2,000 ミリ秒の遅延を生じさせます。To ensure that the example illustrates the difference between the synchronized and unsynchronized access (which could be the case if each method call completes rapidly), the method includes a random delay: for threads whose Thread.ManagedThreadId property is even, the method calls Thread.Sleep to introduce a delay of 2,000 milliseconds. SyncResource クラスはパブリックではなく、同期されたリソースでロックを取得するクライアント コードは存在しないので、内部クラス自体がロックを取得することに注意してください。Note that, because the SyncResource class is not public, none of the client code takes a lock on the synchronized resource; the internal class itself takes the lock. これにより、悪意のあるコードがパブリック オブジェクトでロックを取得するのを防ぐことができます。This prevents malicious code from taking a lock on a public object.

using System;
using System.Threading;

internal class SyncResource
{
    // Use a monitor to enforce synchronization.
    public void Access()
    {
        lock(this) {
            Console.WriteLine("Starting synchronized resource access on thread #{0}",
                              Thread.CurrentThread.ManagedThreadId);
            if (Thread.CurrentThread.ManagedThreadId % 2 == 0)
                Thread.Sleep(2000);

            Thread.Sleep(200);
            Console.WriteLine("Stopping synchronized resource access on thread #{0}",
                              Thread.CurrentThread.ManagedThreadId);
        }
    }
}

internal class UnSyncResource
{
    // Do not enforce synchronization.
    public void Access()
    {
        Console.WriteLine("Starting unsynchronized resource access on Thread #{0}",
                          Thread.CurrentThread.ManagedThreadId);
        if (Thread.CurrentThread.ManagedThreadId % 2 == 0)
            Thread.Sleep(2000);

        Thread.Sleep(200);
        Console.WriteLine("Stopping unsynchronized resource access on thread #{0}",
                          Thread.CurrentThread.ManagedThreadId);
    }
}

public class App
{
    private static int numOps;
    private static AutoResetEvent opsAreDone = new AutoResetEvent(false);
    private static SyncResource SyncRes = new SyncResource();
    private static UnSyncResource UnSyncRes = new UnSyncResource();

   public static void Main()
   {
        // Set the number of synchronized calls.
        numOps = 5;
        for (int ctr = 0; ctr <= 4; ctr++)
            ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource));

        // Wait until this WaitHandle is signaled.
        opsAreDone.WaitOne();
        Console.WriteLine("\t\nAll synchronized operations have completed.\n");

        // Reset the count for unsynchronized calls.
        numOps = 5;
        for (int ctr = 0; ctr <= 4; ctr++)
            ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource));

        // Wait until this WaitHandle is signaled.
        opsAreDone.WaitOne();
        Console.WriteLine("\t\nAll unsynchronized thread operations have completed.\n");
   }

    static void SyncUpdateResource(Object state)
    {
        // Call the internal synchronized method.
        SyncRes.Access();

        // Ensure that only one thread can decrement the counter at a time.
        if (Interlocked.Decrement(ref numOps) == 0)
            // Announce to Main that in fact all thread calls are done.
            opsAreDone.Set();
    }

    static void UnSyncUpdateResource(Object state)
    {
        // Call the unsynchronized method.
        UnSyncRes.Access();

        // Ensure that only one thread can decrement the counter at a time.
        if (Interlocked.Decrement(ref numOps) == 0)
            // Announce to Main that in fact all thread calls are done.
            opsAreDone.Set();
    }
}
// The example displays output like the following:
//    Starting synchronized resource access on thread #6
//    Stopping synchronized resource access on thread #6
//    Starting synchronized resource access on thread #7
//    Stopping synchronized resource access on thread #7
//    Starting synchronized resource access on thread #3
//    Stopping synchronized resource access on thread #3
//    Starting synchronized resource access on thread #4
//    Stopping synchronized resource access on thread #4
//    Starting synchronized resource access on thread #5
//    Stopping synchronized resource access on thread #5
//
//    All synchronized operations have completed.
//
//    Starting unsynchronized resource access on Thread #7
//    Starting unsynchronized resource access on Thread #9
//    Starting unsynchronized resource access on Thread #10
//    Starting unsynchronized resource access on Thread #6
//    Starting unsynchronized resource access on Thread #3
//    Stopping unsynchronized resource access on thread #7
//    Stopping unsynchronized resource access on thread #9
//    Stopping unsynchronized resource access on thread #3
//    Stopping unsynchronized resource access on thread #10
//    Stopping unsynchronized resource access on thread #6
//
//    All unsynchronized thread operations have completed.
Imports System.Threading

Friend Class SyncResource
    ' Use a monitor to enforce synchronization.
    Public Sub Access()
        SyncLock Me
            Console.WriteLine("Starting synchronized resource access on thread #{0}",
                              Thread.CurrentThread.ManagedThreadId)
            If Thread.CurrentThread.ManagedThreadId Mod 2 = 0 Then
                Thread.Sleep(2000)
            End If
            Thread.Sleep(200)
            Console.WriteLine("Stopping synchronized resource access on thread #{0}",
                              Thread.CurrentThread.ManagedThreadId)
        End SyncLock
    End Sub
End Class

Friend Class UnSyncResource
    ' Do not enforce synchronization.
    Public Sub Access()
        Console.WriteLine("Starting unsynchronized resource access on Thread #{0}",
                          Thread.CurrentThread.ManagedThreadId)
        If Thread.CurrentThread.ManagedThreadId Mod 2 = 0 Then
            Thread.Sleep(2000)
        End If
        Thread.Sleep(200)
        Console.WriteLine("Stopping unsynchronized resource access on thread #{0}",
                          Thread.CurrentThread.ManagedThreadId)
    End Sub
End Class

Public Module App
    Private numOps As Integer
    Private opsAreDone As New AutoResetEvent(False)
    Private SyncRes As New SyncResource()
    Private UnSyncRes As New UnSyncResource()

    Public Sub Main()
        ' Set the number of synchronized calls.
        numOps = 5
        For ctr As Integer = 0 To 4
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf SyncUpdateResource))
        Next
        ' Wait until this WaitHandle is signaled.
        opsAreDone.WaitOne()
        Console.WriteLine(vbTab + vbNewLine + "All synchronized operations have completed.")
        Console.WriteLine()

        numOps = 5
        ' Reset the count for unsynchronized calls.
        For ctr As Integer = 0 To 4
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf UnSyncUpdateResource))
        Next

        ' Wait until this WaitHandle is signaled.
        opsAreDone.WaitOne()
        Console.WriteLine(vbTab + vbNewLine + "All unsynchronized thread operations have completed.")
    End Sub

    Sub SyncUpdateResource()
        ' Call the internal synchronized method.
        SyncRes.Access()

        ' Ensure that only one thread can decrement the counter at a time.
        If Interlocked.Decrement(numOps) = 0 Then
            ' Announce to Main that in fact all thread calls are done.
            opsAreDone.Set()
        End If
    End Sub

    Sub UnSyncUpdateResource()
        ' Call the unsynchronized method.
        UnSyncRes.Access()

        ' Ensure that only one thread can decrement the counter at a time.
        If Interlocked.Decrement(numOps) = 0 Then
            ' Announce to Main that in fact all thread calls are done.
            opsAreDone.Set()
        End If
    End Sub
End Module
' The example displays output like the following:
'    Starting synchronized resource access on thread #6
'    Stopping synchronized resource access on thread #6
'    Starting synchronized resource access on thread #7
'    Stopping synchronized resource access on thread #7
'    Starting synchronized resource access on thread #3
'    Stopping synchronized resource access on thread #3
'    Starting synchronized resource access on thread #4
'    Stopping synchronized resource access on thread #4
'    Starting synchronized resource access on thread #5
'    Stopping synchronized resource access on thread #5
'
'    All synchronized operations have completed.
'
'    Starting unsynchronized resource access on Thread #7
'    Starting unsynchronized resource access on Thread #9
'    Starting unsynchronized resource access on Thread #10
'    Starting unsynchronized resource access on Thread #6
'    Starting unsynchronized resource access on Thread #3
'    Stopping unsynchronized resource access on thread #7
'    Stopping unsynchronized resource access on thread #9
'    Stopping unsynchronized resource access on thread #3
'    Stopping unsynchronized resource access on thread #10
'    Stopping unsynchronized resource access on thread #6
'
'    All unsynchronized thread operations have completed.

例では、リソースにアクセスしようとするスレッドの数を定義する変数 numOps を定義します。The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. アプリケーション スレッドは、同期アクセスの場合も非同期アクセスの場合もそれぞれ 5 回、ThreadPool.QueueUserWorkItem(WaitCallback) メソッドを呼び出します。The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. ThreadPool.QueueUserWorkItem(WaitCallback) メソッドにはパラメーターが 1 つしかありません。パラメーターを受け入れず値を返さないデリゲートです。The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. 同期アクセスの場合は SyncUpdateResource メソッドを呼び出し、非同期アクセスの場合は UnSyncUpdateResource メソッドを呼び出します。For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. メソッドが呼び出されるたびに、アプリケーションスレッドはsystem.threading.waithandle.waitoneメソッドを呼び出して、 AutoResetEventインスタンスがシグナル状態になるまでブロックされるようにします。After each set of method calls, the application thread calls the AutoResetEvent.WaitOne method so that it blocks until the AutoResetEvent instance is signaled.

SyncUpdateResource メソッドを呼び出すたびに、内部 SyncResource.Access メソッドが呼び出され、Interlocked.Decrement メソッドが呼び出されて、numOps カウンターがデクリメントされます。Each call to the SyncUpdateResource method calls the internal SyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. Interlocked.Decrementメソッドは、カウンターをデクリメントするために使用されます。それ以外の場合、最初のスレッドのデクリメントされた値が変数に格納される前に、2番目のスレッドが値にアクセスすることはありません。The Interlocked.Decrement method Is used to decrement the counter, because otherwise you cannot be certain that a second thread will access the value before a first thread's decremented value has been stored in the variable. 最後に同期されたワーカースレッドがカウンターを0に減らした場合、同期されたすべてのスレッドSyncUpdateResourceがリソースへEventWaitHandle.Setのアクセスを完了したことを示します。メソッドは、メインスレッドに続行を通知するメソッドを呼び出します。例外.When the last synchronized worker thread decrements the counter to zero, indicating that all synchronized threads have completed accessing the resource, the SyncUpdateResource method calls the EventWaitHandle.Set method, which signals the main thread to continue execution.

UnSyncUpdateResource メソッドを呼び出すたびに、内部 UnSyncResource.Access メソッドが呼び出され、Interlocked.Decrement メソッドが呼び出されて、numOps カウンターがデクリメントされます。Each call to the UnSyncUpdateResource method calls the internal UnSyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. この場合も、 Interlocked.Decrementメソッドを使用してカウンターをデクリメントすることで、最初のスレッドのデクリメントされた値が変数に割り当てられる前に、2番目のスレッドが値にアクセスしないようにします。Once again, the Interlocked.Decrement method Is used to decrement the counter to ensure that a second thread does not access the value before a first thread's decremented value has been assigned to the variable. 最後の同期されていないワーカースレッドがカウンターをゼロにデクリメントすると、そのリソースUnSyncUpdateResourceにアクセスする必要がある非同期のスレッドがないことが示されます。このメソッドは、メインスレッドに実行を継続するように通知するEventWaitHandle.Setメソッドを呼び出します。.When the last unsynchronized worker thread decrements the counter to zero, indicating that no more unsynchronized threads need to access the resource, the UnSyncUpdateResource method calls the EventWaitHandle.Set method, which signals the main thread to continue execution.

例の出力からわかるように、同期アクセスでは、呼び出し元スレッドが保護リソースを終了してからでないと別のスレッドがそれにアクセスできません。つまり各スレッドはその先行処理を待機します。As the output from the example shows, synchronized access ensures that the calling thread exits the protected resource before another thread can access it; each thread waits on its predecessor. その一方で、ロックがない UnSyncResource.Access メソッドは、スレッドが到達する順序で呼び出されます。On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

注釈

クラスを使用すると、、、およびMonitor.ExitMonitor.Enter各メソッドを呼び出すことによって、 Monitor.TryEnter特定のオブジェクトのロックを取得して解放することにより、コード領域へのアクセスを同期できます。 MonitorThe Monitor class allows you to synchronize access to a region of code by taking and releasing a lock on a particular object by calling the Monitor.Enter, Monitor.TryEnter, and Monitor.Exit methods. オブジェクトロックは、一般にクリティカルセクションと呼ばれる、コードブロックへのアクセスを制限する機能を提供します。Object locks provide the ability to restrict access to a block of code, commonly called a critical section. スレッドがオブジェクトのロックを所有している間、他のスレッドがそのロックを取得することはできません。While a thread owns the lock for an object, no other thread can acquire that lock. また、 Monitorクラスを使用して、他のスレッドが別のロックされたオブジェクトを使用してコードを実行している場合を除き、ロック所有者によって実行されるアプリケーションコードのセクションに他のスレッドがアクセスできないようにすることもできます。You can also use the Monitor class to ensure that no other thread is allowed to access a section of application code being executed by the lock owner, unless the other thread is executing the code using a different locked object.

この記事の内容:In this article:

Monitor クラス:概要 The Monitor class: An overview
Lock オブジェクト The lock object
クリティカルセクション The critical section
Pulse、System.threading.monitor.pulseall、Wait Pulse, PulseAll, and Wait
モニターと待機ハンドルMonitors and wait handles

Monitor クラス:概要The Monitor class: An overview

Monitorには次の機能があります。Monitor has the following features:

  • 要求時にオブジェクトに関連付けられています。It is associated with an object on demand.

  • バインド解除されているため、任意のコンテキストから直接呼び出すことができます。It is unbound, which means it can be called directly from any context.

  • Monitorクラスのインスタンスを作成することはできません。 Monitorクラスのメソッドはすべて静的です。An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. 各メソッドには、クリティカルセクションへのアクセスを制御する同期されたオブジェクトが渡されます。Each method is passed the synchronized object that controls access to the critical section.

注意

値型Monitorではなく、文字列以外のオブジェクト (つまり、以外Stringの参照型) をロックするには、クラスを使用します。Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. 詳細については、この記事Enterで後述する「メソッドとlock オブジェクトのオーバーロード」を参照してください。For details, see the overloads of the Enter method and The lock object section later in this article.

次の表では、同期されたオブジェクトにアクセスするスレッドで実行できる操作について説明します。The following table describes the actions that can be taken by threads that access synchronized objects:

アクションAction 説明Description
EnterTryEnterEnter, TryEnter オブジェクトのロックを取得します。Acquires a lock for an object. この操作は、クリティカルセクションの開始を示すこともできます。This action also marks the beginning of a critical section. 他のスレッドは、別のロックされたオブジェクトを使用してクリティカルセクションの命令を実行しない限り、クリティカルセクションに入ることはできません。No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait オブジェクトのロックを解除し、他のスレッドがオブジェクトにロックしてアクセスできるようにします。Releases the lock on an object in order to permit other threads to lock and access the object. 呼び出し元のスレッドは、別のスレッドがオブジェクトにアクセスしている間、待機します。The calling thread waits while another thread accesses the object. パルスシグナルは、オブジェクトの状態に対する変更について、待機中のスレッドに通知するために使用されます。Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse(シグナル)、PulseAllPulse (signal), PulseAll 1つ以上の待機中のスレッドにシグナルを送信します。Sends a signal to one or more waiting threads. シグナルは、ロックされたオブジェクトの状態が変化したこと、およびロックの所有者がロックを解放する準備ができたことを待機中のスレッドに通知します。The signal notifies a waiting thread that the state of the locked object has changed, and the owner of the lock is ready to release the lock. 待機中のスレッドは、オブジェクトのロックを最終的に受け取る可能性があるように、オブジェクトの準備完了キューに配置されます。The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. スレッドがロック状態になったら、オブジェクトの新しい状態をチェックして、必要な状態に達したかどうかを確認できます。Once the thread has the lock, it can check the new state of the object to see if the required state has been reached.
Exit オブジェクトのロックを解放します。Releases the lock on an object. この操作は、ロックされたオブジェクトによって保護されているクリティカルセクションの終了もマークします。This action also marks the end of a critical section protected by the locked object.

以降では、メソッドEnterTryEnterメソッドに2つのオーバーロードセットがあります。 .NET Framework 4.NET Framework 4Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. 1組のオーバーロードにはref 、ロックを取得するときに例外がスローされた場合trueでも、ロックが取得された場合にはアトミックにに設定される (in C#) またはByRef (Visual Basic) Booleanパラメーターがあります。One set of overloads has a ref (in C#) or ByRef (in Visual Basic) Boolean parameter that is atomically set to true if the lock is acquired, even if an exception is thrown when acquiring the lock. ロックが保護されているリソースが一貫性のある状態ではない場合でも、常にロックを解放することが重要な場合は、これらのオーバーロードを使用します。Use these overloads if it is critical to release the lock in all cases, even when the resources the lock is protecting might not be in a consistent state.

Lock オブジェクトThe lock object

Monitor クラスは、クリティカルstaticセクションへC#のアクセスSharedを制御するオブジェクトを操作する (では) メソッドまたは (Visual Basic) メソッドで構成されます。The Monitor class consists of static (in C#) or Shared (in Visual Basic) methods that operate on an object that controls access to the critical section. 同期されたオブジェクトごとに、次の情報が保持されます。The following information is maintained for each synchronized object:

  • 現在ロックを保持しているスレッドへの参照。A reference to the thread that currently holds the lock.

  • 準備完了のキューへの参照。ロックを取得する準備ができているスレッドを格納します。A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • 待機中のキューへの参照。ロックされたオブジェクトの状態の変更の通知を待機しているスレッドを格納します。A reference to a waiting queue, which contains the threads that are waiting for notification of a change in the state of the locked object.

Monitor は値型ではなく、オブジェクト (つまり、参照型) をロックします。Monitor locks objects (that is, reference types), not value types. 値型を EnterExit に渡すことができますが、値型は呼び出しごとに個別にボックス化されます。While you can pass a value type to Enter and Exit, it is boxed separately for each call. 呼び出しごとに個別のオブジェクトが作成されるので、Enter は決してコードをブロックすることはなく、保護していると想定しているコードは実際には同期されません。Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. さらに、Exit に渡されたオブジェクトは Enter に渡されたオブジェクトとは異なるため、Monitor は「オブジェクトの同期メソッドが、コードの非同期ブロックから呼び出されました。」というメッセージとともに SynchronizationLockException 例外をスローします。In addition, the object passed to Exit is different from the object passed to Enter, so Monitor throws SynchronizationLockException exception with the message "Object synchronization method was called from an unsynchronized block of code."

この問題を説明する例を次に示します。The following example illustrates this problem. 10 個のタスクが起動され、それぞれが 250 ミリ秒間スリープ状態になります。It launches ten tasks, each of which just sleeps for 250 milliseconds. 次に、各タスクはカウンター変数である nTasks を更新します。これは実際に起動、実行されるタスクの数をカウントするためのものです。Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. nTasks は複数のタスクで同時に更新可能なグローバル変数なので、複数のタスクによる同時変更を防止するためにモニターを使用します。Because nTasks is a global variable that can be updated by multiple tasks simultaneously, a monitor is used to protect it from simultaneous modification by multiple tasks. しかし、例に示す出力のように、各タスクは SynchronizationLockException 例外をスローします。However, as the output from the example shows, each of the tasks throws a SynchronizationLockException exception.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {

      int nTasks = 0;
      List<Task> tasks = new List<Task>();
      
      try {
         for (int ctr = 0; ctr < 10; ctr++)
            tasks.Add(Task.Run( () => { // Instead of doing some work, just sleep.
                                        Thread.Sleep(250);
                                        // Increment the number of tasks.
                                        Monitor.Enter(nTasks);
                                        try {
                                           nTasks += 1;
                                        }
                                        finally {
                                           Monitor.Exit(nTasks);
                                        }
                                      } ));
         Task.WaitAll(tasks.ToArray());
         Console.WriteLine("{0} tasks started and executed.", nTasks);
      }
      catch (AggregateException e) {
         String msg = String.Empty;
         foreach (var ie in e.InnerExceptions) {
            Console.WriteLine("{0}", ie.GetType().Name);
            if (! msg.Contains(ie.Message))
               msg += ie.Message + Environment.NewLine;
         }
         Console.WriteLine("\nException Message(s):");
         Console.WriteLine(msg);
      }
   }
}
// The example displays the following output:
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//    SynchronizationLockException
//
//    Exception Message(s):
//    Object synchronization method was called from an unsynchronized block of code.
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim nTasks As Integer = 0
      Dim tasks As New List(Of Task)()

      Try
         For ctr As Integer = 0 To 9
            tasks.Add(Task.Run( Sub()
                                   ' Instead of doing some work, just sleep.
                                   Thread.Sleep(250)
                                   ' Increment the number of tasks.
                                   Monitor.Enter(nTasks)
                                   Try
                                      nTasks += 1
                                   Finally
                                      Monitor.Exit(nTasks)
                                   End Try
                                End Sub))
         Next
         Task.WaitAll(tasks.ToArray())
         Console.WriteLine("{0} tasks started and executed.", nTasks)
      Catch e As AggregateException
         Dim msg AS String = String.Empty
         For Each ie In e.InnerExceptions
            Console.WriteLine("{0}", ie.GetType().Name)
            If Not msg.Contains(ie.Message) Then
               msg += ie.Message + Environment.NewLine
            End If
         Next
         Console.WriteLine(vbCrLf + "Exception Message(s):")
         Console.WriteLine(msg)
      End Try
   End Sub
End Module
' The example displays the following output:
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'    SynchronizationLockException
'
'    Exception Message(s):
'    Object synchronization method was called from an unsynchronized block of code.

各タスクの Monitor.Enter メソッドに対する呼び出しの前に nTasks 変数がボックス化されるため、各タスクは SynchronizationLockException 例外をスローします。Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. つまり、各メソッドの呼び出しは他のメソッドから独立している個別の変数に渡されます。In other words, each method call is passed a separate variable that is independent of the others. nTasksMonitor.Exit メソッドへの呼び出しで再びボックス化されます。nTasks is boxed again in the call to the Monitor.Exit method. こうして 10 個の新しいボックス化された変数が作成されます。これらは互いに独立したものであり、nTasks からも Monitor.Enter メソッドへの呼び出しで作成された 10 個のボックス化された変数からも独立しています。Once again, this creates ten new boxed variables, which are independent of each other, nTasks, and the ten boxed variables created in the call to the Monitor.Enter method. それで、以前ロックされていなかった新規に作成された変数のロックを解放しようとしているため、例外がスローされます。The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

次の例に示すように、EnterExit の呼び出しの前に値型の変数をボックス化したり、ボックス化された同じオブジェクトを両方のメソッドに渡したりできますが、これを行う利点はありません。Although you can box a value type variable before calling Enter and Exit, as shown in the following example, and pass the same boxed object to both methods, there is no advantage to doing this. ボックス化解除された変数への変更は、ボックス化されたコピーには反映されません。またボックス化されたコピーの値を変更する方法はありません。Changes to the unboxed variable are not reflected in the boxed copy, and there is no way to change the value of the boxed copy.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {

      int nTasks = 0;
      object o = nTasks;
      List<Task> tasks = new List<Task>();
      
      try {
         for (int ctr = 0; ctr < 10; ctr++)
            tasks.Add(Task.Run( () => { // Instead of doing some work, just sleep.
                                        Thread.Sleep(250);
                                        // Increment the number of tasks.
                                        Monitor.Enter(o);
                                        try {
                                           nTasks++;
                                        }
                                        finally {
                                           Monitor.Exit(o);
                                        }
                                      } ));
         Task.WaitAll(tasks.ToArray());
         Console.WriteLine("{0} tasks started and executed.", nTasks);
      }
      catch (AggregateException e) {
         String msg = String.Empty;
         foreach (var ie in e.InnerExceptions) {
            Console.WriteLine("{0}", ie.GetType().Name);
            if (! msg.Contains(ie.Message))
               msg += ie.Message + Environment.NewLine;
         }
         Console.WriteLine("\nException Message(s):");
         Console.WriteLine(msg);
      }
   }
}
// The example displays the following output:
//        10 tasks started and executed.
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim nTasks As Integer = 0
      Dim o As Object = nTasks
      Dim tasks As New List(Of Task)()

      Try
         For ctr As Integer = 0 To 9
            tasks.Add(Task.Run( Sub()
                                   ' Instead of doing some work, just sleep.
                                   Thread.Sleep(250)
                                   ' Increment the number of tasks.
                                   Monitor.Enter(o)
                                   Try
                                      nTasks += 1
                                   Finally
                                      Monitor.Exit(o)
                                   End Try
                                End Sub))
         Next
         Task.WaitAll(tasks.ToArray())
         Console.WriteLine("{0} tasks started and executed.", nTasks)
      Catch e As AggregateException
         Dim msg AS String = String.Empty
         For Each ie In e.InnerExceptions
            Console.WriteLine("{0}", ie.GetType().Name)
            If Not msg.Contains(ie.Message) Then
               msg += ie.Message + Environment.NewLine
            End If
         Next
         Console.WriteLine(vbCrLf + "Exception Message(s):")
         Console.WriteLine(msg)
      End Try
   End Sub
End Module
' The example displays the following output:
'       10 tasks started and executed.

同期するオブジェクトを選択するときは、プライベートまたは内部のオブジェクトのみをロックする必要があります。When selecting an object on which to synchronize, you should lock only on private or internal objects. 外部オブジェクトをロックするとデッドロックが発生する可能性があります。これは、関連のないコードが、異なる目的でロックする同じオブジェクトを選択する可能性があるためです。Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

ロックに使用されるオブジェクトがからMarshalByRefObject派生している場合は、複数のアプリケーションドメイン内のオブジェクトを同期できます。Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

クリティカルセクションThe critical section

クリティカルセクションのExit開始と終了をマークするには、メソッドとメソッドを使用します。EnterUse the Enter and Exit methods to mark the beginning and end of a critical section.

注意

メソッドEnter C# メソッドによって提供される機能は、の lock ステートメントと Visual Basic の SyncLock ステートメントによって提供される機能と同じですが、言語構成要素がExit Monitor.Enter(Object, Boolean)メソッドのMonitor.Exitオーバーロードと、 try...finallyThe functionality provided by the Enter and Exit methods is identical to that provided by the lock statement in C# and the SyncLock statement in Visual Basic, except that the language constructs wrap the Monitor.Enter(Object, Boolean) method overload and the Monitor.Exit method in a tryfinally ブロックして、モニターがリリースされていることを確認します。block to ensure that the monitor is released.

クリティカルセクションが一連の連続する命令である場合、 Enterメソッドによって取得されたロックは、ロックされたオブジェクトを使用して、囲まれたコードを1つのスレッドだけが実行できることを保証します。If the critical section is a set of contiguous instructions, then the lock acquired by the Enter method guarantees that only a single thread can execute the enclosed code with the locked object. この場合は、そのコードをtryブロックに配置し、 Exitメソッドfinallyへの呼び出しをブロックに配置することをお勧めします。In this case, we recommend that you place that code in a try block and place the call to the Exit method in a finally block. これにより、例外が発生しても必ずロックが解放されるようになります。This ensures that the lock is released even if an exception occurs. 次のコードは、このパターンを示しています。The following code fragment illustrates this pattern.

// Define the lock object.
var obj = new Object();

// Define the critical section.
Monitor.Enter(obj);
try {
   // Code to execute one thread at a time.
}
// catch blocks go here.
finally {
   Monitor.Exit(obj);
}
' Define the lock object.
Dim obj As New Object()

' Define the critical section.
Monitor.Enter(obj)
Try 
   ' Code to execute one thread at a time.

' catch blocks go here.
Finally 
   Monitor.Exit(obj)
End Try

この機能は、通常、クラスの静的メソッドまたはインスタンスメソッドへのアクセスを同期するために使用されます。This facility is typically used to synchronize access to a static or instance method of a class.

クリティカルセクションがメソッド全体にまたがっている場合は、 System.Runtime.CompilerServices.MethodImplAttributeをメソッドに配置し、のSystem.Runtime.CompilerServices.MethodImplAttributeコンストラクターでSynchronized値を指定することによって、ロック機能を実現できます。If a critical section spans an entire method, the locking facility can be achieved by placing the System.Runtime.CompilerServices.MethodImplAttribute on the method, and specifying the Synchronized value in the constructor of System.Runtime.CompilerServices.MethodImplAttribute. この属性を使用する場合、 EnterメソッドExitとメソッドの呼び出しは必要ありません。When you use this attribute, the Enter and Exit method calls are not needed. 次のコードは、このパターンを示しています。The following code fragment illustrates this pattern:

[MethodImplAttribute(MethodImplOptions.Synchronized)]
void MethodToLock()
{
   // Method implementation.
} 
<MethodImplAttribute(MethodImplOptions.Synchronized)>
Sub MethodToLock()
   ' Method implementation.
End Sub 

属性によって、メソッドが戻るまで、ロックを保持するために、現在のスレッドに注意してください。ロックがすぐに解放する場合は、使用、Monitorクラスの C#ロックステートメント、または Visual Basic SyncLock属性ではなく、メソッド内でステートメント。Note that the attribute causes the current thread to hold the lock until the method returns; if the lock can be released sooner, use the Monitor class, the C# lock statement, or the Visual Basic SyncLock statement inside of the method instead of the attribute.

指定されたオブジェクトをEnterロックExitして解放するステートメントとステートメントは、メンバーまたはクラスの境界の間またはその両方になる可能性がありますが、この方法はお勧めできません。While it is possible for the Enter and Exit statements that lock and release a given object to cross member or class boundaries or both, this practice is not recommended.

Pulse、System.threading.monitor.pulseall、WaitPulse, PulseAll, and Wait

スレッドがロックを所有していて、ロックによって保護されているクリティカルセクションMonitor.WaitMonitor.Pulse入るとMonitor.PulseAll 、、、およびの各メソッドを呼び出すことができます。Once a thread owns the lock and has entered the critical section that the lock protects, it can call the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods.

ロックを保持するスレッドがを呼びWait出すと、ロックが解放され、スレッドが同期されたオブジェクトの待機キューに追加されます。When the thread that holds the lock calls Wait, the lock is released and the thread is added to the waiting queue of the synchronized object. 準備完了キュー内の最初のスレッド (存在する場合) は、ロックを取得し、クリティカルセクションに入ります。The first thread in the ready queue, if any, acquires the lock and enters the critical section. が呼び出さWaitれたスレッドは、 Monitor.PulseAllメソッドMonitor.Pulseまたはメソッドがロックを保持しているスレッドによって呼び出されたときに、待機キューから準備キューに移動されます (移動するには、スレッドが待機キューの先頭にある必要があります)。The thread that called Wait is moved from the waiting queue to the ready queue when either the Monitor.Pulse or the Monitor.PulseAll method is called by the thread that holds the lock (to be moved, the thread must be at the head of the waiting queue). メソッドWaitは、呼び出し元のスレッドがロックを再取得たときにを返します。The Wait method returns when the calling thread reacquires the lock.

ロックを保持するスレッドがを呼びPulse出すと、待機キューの先頭にあるスレッドが、準備完了キューに移動されます。When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. PulseAllメソッドを呼び出すと、すべてのスレッドが待機キューから準備完了キューに移動されます。The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

モニターと待機ハンドルMonitors and wait handles

MonitorクラスとWaitHandleオブジェクトの使用方法の違いに注意することが重要です。It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • Monitorクラスは純粋に管理され、完全に移植可能であり、オペレーティングシステムのリソース要件に関してより効率的な場合があります。The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • WaitHandle オブジェクトはオペレーティング システムの待機可能オブジェクトを表しており、マネージドとアンマネージド コード間で同期するのに便利です。また一度に多くのオブジェクトを待機できる機能などの高度なオペレーティング システム機能を公開します。WaitHandle objects represent operating-system waitable objects, are useful for synchronizing between managed and unmanaged code, and expose some advanced operating-system features like the ability to wait on many objects at once.

プロパティ

LockContentionCount LockContentionCount LockContentionCount LockContentionCount

モニターのロックを取得しようとするときに、接続があった回数を取得します。Gets the number of times there was contention when trying to take the monitor's lock.

メソッド

Enter(Object) Enter(Object) Enter(Object) Enter(Object)

指定したオブジェクトの排他ロックを取得します。Acquires an exclusive lock on the specified object.

Enter(Object, Boolean) Enter(Object, Boolean) Enter(Object, Boolean) Enter(Object, Boolean)

指定したオブジェクトの排他ロックを取得し、ロックが取得されたかどうかを示す値をアトミックに設定します。Acquires an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

Exit(Object) Exit(Object) Exit(Object) Exit(Object)

指定したオブジェクトの排他ロックを解放します。Releases an exclusive lock on the specified object.

IsEntered(Object) IsEntered(Object) IsEntered(Object) IsEntered(Object)

現在のスレッドが指定したオブジェクトのロックを保持しているかどうかを判断します。Determines whether the current thread holds the lock on the specified object.

Pulse(Object) Pulse(Object) Pulse(Object) Pulse(Object)

ロックされたオブジェクトの状態が変更されたことを、待機キュー内のスレッドに通知します。Notifies a thread in the waiting queue of a change in the locked object's state.

PulseAll(Object) PulseAll(Object) PulseAll(Object) PulseAll(Object)

オブジェクトの状態が変更されたことを、待機中のすべてのスレッドに通知します。Notifies all waiting threads of a change in the object's state.

TryEnter(Object, TimeSpan, Boolean) TryEnter(Object, TimeSpan, Boolean) TryEnter(Object, TimeSpan, Boolean) TryEnter(Object, TimeSpan, Boolean)

指定したオブジェクトの排他ロックの取得を指定した時間にわたって試み、ロックが取得されたかどうかを示す値をアトミックに設定します。Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

TryEnter(Object, Int32, Boolean) TryEnter(Object, Int32, Boolean) TryEnter(Object, Int32, Boolean) TryEnter(Object, Int32, Boolean)

指定したオブジェクトの排他ロックの取得を指定したミリ秒間試み、ロックが取得されたかどうかを示す値をアトミックに設定します。Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

TryEnter(Object, TimeSpan) TryEnter(Object, TimeSpan) TryEnter(Object, TimeSpan) TryEnter(Object, TimeSpan)

指定した時間内に、指定したオブジェクトの排他ロックの取得を試みます。Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object.

TryEnter(Object, Boolean) TryEnter(Object, Boolean) TryEnter(Object, Boolean) TryEnter(Object, Boolean)

指定したオブジェクトの排他ロックの取得を試み、ロックが取得されたかどうかを示す値をアトミックに設定します。Attempts to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

TryEnter(Object) TryEnter(Object) TryEnter(Object) TryEnter(Object)

指定したオブジェクトの排他ロックの取得を試みます。Attempts to acquire an exclusive lock on the specified object.

TryEnter(Object, Int32) TryEnter(Object, Int32) TryEnter(Object, Int32) TryEnter(Object, Int32)

指定したミリ秒間に、指定したオブジェクトの排他ロックの取得を試みます。Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object.

Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean)

オブジェクトのロックを解放し、現在のスレッドがロックを再取得するまでそのスレッドをブロックします。Releases the lock on an object and blocks the current thread until it reacquires the lock. 指定されたタイムアウト期限を過ぎると、スレッドは実行待ちキューに入ります。If the specified time-out interval elapses, the thread enters the ready queue. このメソッドは、コンテキストの同期ドメイン (同期されたコンテキストの場合) が待機の前に終了し、後で再取得されるかどうかも指定します。This method also specifies whether the synchronization domain for the context (if in a synchronized context) is exited before the wait and reacquired afterward.

Wait(Object) Wait(Object) Wait(Object) Wait(Object)

オブジェクトのロックを解放し、現在のスレッドがロックを再取得するまでそのスレッドをブロックします。Releases the lock on an object and blocks the current thread until it reacquires the lock.

Wait(Object, Int32) Wait(Object, Int32) Wait(Object, Int32) Wait(Object, Int32)

オブジェクトのロックを解放し、現在のスレッドがロックを再取得するまでそのスレッドをブロックします。Releases the lock on an object and blocks the current thread until it reacquires the lock. 指定されたタイムアウト期限を過ぎると、スレッドは実行待ちキューに入ります。If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, TimeSpan) Wait(Object, TimeSpan) Wait(Object, TimeSpan) Wait(Object, TimeSpan)

オブジェクトのロックを解放し、現在のスレッドがロックを再取得するまでそのスレッドをブロックします。Releases the lock on an object and blocks the current thread until it reacquires the lock. 指定されたタイムアウト期限を過ぎると、スレッドは実行待ちキューに入ります。If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean)

オブジェクトのロックを解放し、現在のスレッドがロックを再取得するまでそのスレッドをブロックします。Releases the lock on an object and blocks the current thread until it reacquires the lock. 指定されたタイムアウト期限を過ぎると、スレッドは実行待ちキューに入ります。If the specified time-out interval elapses, the thread enters the ready queue. または、待機の前に同期化されたコンテキストの同期ドメインを終了し、ドメインを後で再取得します。Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

適用対象

スレッド セーフ

この型はスレッド セーフです。This type is thread safe.

こちらもご覧ください