Monitor Klasa

Definicja

Zapewnia mechanizm, który synchronizuje dostęp do obiektów.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
Dziedziczenie
Monitor
Atrybuty

Przykłady

Poniższy przykład używa klasy Monitor do synchronizowania dostępu do jednego wystąpienia generatora liczb losowych reprezentowanego przez klasę Random.The following example uses the Monitor class to synchronize access to a single instance of a random number generator represented by the Random class. Przykład tworzy dziesięć zadań, z których każdy jest wykonywany asynchronicznie w wątku puli wątków.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Każde zadanie generuje liczbę losową 10 000, oblicza ich średnią i aktualizuje dwie zmienne na poziomie procedury, które utrzymują liczbę liczb losowych wygenerowanych i ich sumę.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. Po wykonaniu wszystkich zadań te dwie wartości są następnie używane do obliczania średniej.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)

Ponieważ można uzyskać do nich dostęp z dowolnego zadania uruchomionego w wątku puli wątków, należy również zsynchronizować dostęp do zmiennych total i n.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. W tym celu jest używana metoda Interlocked.Add.The Interlocked.Add method is used for this purpose.

Poniższy przykład ilustruje połączone użycie klasy Monitor (zaimplementowane z lock lub SyncLock konstrukcji językowej), klasy Interlocked i klasy AutoResetEvent.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. Definiuje dwie internal (in C#) lub Friend (w Visual Basic) klasy, SyncResource i UnSyncResource, które odpowiednio zapewniają synchronizację i niezsynchronizowany dostęp do zasobu.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. Aby zapewnić, że przykład ilustruje różnicę między synchronizowanym i niezsynchronizowanym dostępem (może to być przypadek, jeśli każde wywołanie metody zostanie szybko zakończone), metoda obejmuje losowe opóźnienie: dla wątków, których właściwość Thread.ManagedThreadId jest parzysta, metoda wywołuje Thread.Sleep, aby wprowadzić opóźnienie 2 000 milisekund.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. Należy zauważyć, że ponieważ Klasa SyncResource nie jest publiczna, żaden kod klienta nie ma blokady w synchronizowanym zasobie; sama klasa wewnętrzna przyjmuje blokadę.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. Zapobiega to zablokowaniu obiektu publicznego przez złośliwy kod.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.

W przykładzie zdefiniowano zmienną numOps, która definiuje liczbę wątków, które będą próbować uzyskać dostęp do zasobu.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. Wątek aplikacji wywołuje metodę ThreadPool.QueueUserWorkItem(WaitCallback) dla synchronizowanego i niesynchronizowanego dostępu pięć razy.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. Metoda ThreadPool.QueueUserWorkItem(WaitCallback) ma jeden parametr, delegat, który nie akceptuje parametrów i nie zwraca żadnej wartości.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. W przypadku dostępu zsynchronizowanego wywołuje metodę SyncUpdateResource; w przypadku niezsynchronizowanego dostępu wywołuje metodę UnSyncUpdateResource.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Po każdym zestawie wywołań metod wątek aplikacji wywołuje metodę AutoResetEvent. WaitOne , aby blokowała do momentu zasygnalizowania wystąpienia 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.

Każde wywołanie metody SyncUpdateResource wywołuje wewnętrzną metodę SyncResource.Access, a następnie wywołuje metodę Interlocked.Decrement, aby zmniejszyć licznik 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. Metoda Interlocked.Decrement jest używana do zmniejszania licznika, ponieważ w przeciwnym razie nie można mieć pewności, że drugi wątek będzie miał dostęp do wartości przed zapisaniem w zmiennej wartości w ramach pierwszego wątku.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. Gdy ostatni zsynchronizowany wątek roboczy zmniejsza licznik do zera, wskazując, że wszystkie zsynchronizowane wątki zakończyły uzyskiwanie dostępu do zasobu, Metoda SyncUpdateResource wywołuje metodę EventWaitHandle.Set, która sygnalizuje główny wątek, aby kontynuować wykonywanie.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.

Każde wywołanie metody UnSyncUpdateResource wywołuje wewnętrzną metodę UnSyncResource.Access, a następnie wywołuje metodę Interlocked.Decrement, aby zmniejszyć licznik 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. Po ponownym uruchomieniu Metoda Interlocked.Decrement jest używana w celu zmniejszenia licznika, aby upewnić się, że drugi wątek nie uzyskuje dostępu do wartości przed przypisaniem do zmiennej wartości typu wartość pierwszego wątku.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. Gdy ostatni niezsynchronizowany wątek roboczy zmniejsza licznik do zera, wskazując, że nie ma więcej niezsynchronizowanych wątków potrzebnych do uzyskania dostępu do zasobu, Metoda UnSyncUpdateResource wywołuje metodę EventWaitHandle.Set, która sygnalizuje główny wątek, aby kontynuować wykonywanie.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.

Jak wynika z przykładu, zsynchronizowany dostęp gwarantuje, że wątek wywołujący opuszcza chroniony zasób, zanim inny wątek będzie mógł uzyskać do niego dostęp. Każdy wątek czeka na jego poprzednika.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. Z drugiej strony, bez blokady, Metoda UnSyncResource.Access jest wywoływana w kolejności, w której wątki docierają do niego.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Uwagi

Klasa Monitor pozwala synchronizować dostęp do regionu kodu przez pobranie i zwolnienie blokady dla określonego obiektu przez wywołanie metod Monitor.Enter, Monitor.TryEnteri Monitor.Exit.The 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. Blokady obiektów zapewniają możliwość ograniczenia dostępu do bloku kodu, który jest często określany jako sekcja krytyczna.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. Gdy wątek jest właścicielem blokady obiektu, żaden inny wątek nie może uzyskać tej blokady.While a thread owns the lock for an object, no other thread can acquire that lock. Można również użyć klasy Monitor, aby upewnić się, że żaden inny wątek nie może uzyskać dostępu do sekcji kodu aplikacji wykonywanej przez właściciela blokady, chyba że inny wątek wykonuje kod przy użyciu innego zablokowanego obiektu.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.

W tym artykule:In this article:

Klasa monitora: przegląd The Monitor class: An overview
Obiekt blokady The lock object
Sekcja krytyczna The critical section
Puls, PulseAll i oczekiwania Pulse, PulseAll, and Wait
Monitory i uchwyty oczekiwaniaMonitors and wait handles

Klasa monitora: przeglądThe Monitor class: An overview

Monitor ma następujące funkcje:Monitor has the following features:

  • Jest ona skojarzona z obiektem na żądanie.It is associated with an object on demand.

  • Jest on niepowiązany, co oznacza, że może być wywoływana bezpośrednio z dowolnego kontekstu.It is unbound, which means it can be called directly from any context.

  • Nie można utworzyć wystąpienia klasy Monitor; metody klasy Monitor są statyczne.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. Każda metoda jest przenoszona do synchronizowanego obiektu, który kontroluje dostęp do sekcji krytycznej.Each method is passed the synchronized object that controls access to the critical section.

Uwaga

Użyj klasy Monitor, aby zablokować obiekty inne niż ciągi (czyli typy odwołań inne niż String), a nie typy wartości.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Aby uzyskać szczegółowe informacje, zobacz przeciążanie metody Enter i Zablokuj obiekt w dalszej części tego artykułu.For details, see the overloads of the Enter method and The lock object section later in this article.

W poniższej tabeli opisano akcje, które mogą być podejmowane przez wątki, które uzyskują dostęp do synchronizowanych obiektów:The following table describes the actions that can be taken by threads that access synchronized objects:

AkcjaAction OpisDescription
Enter, TryEnterEnter, TryEnter Uzyskuje blokadę dla obiektu.Acquires a lock for an object. Ta akcja oznacza również początek sekcji krytycznej.This action also marks the beginning of a critical section. Żaden inny wątek nie może wejść do sekcji krytycznej, chyba że wykonuje instrukcje w sekcji krytycznej przy użyciu innego zablokowanego obiektu.No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait Zwalnia blokadę obiektu w celu zezwolenia innym wątkom na zablokowanie i uzyskanie dostępu do obiektu.Releases the lock on an object in order to permit other threads to lock and access the object. Wątek wywołujący czeka, gdy inny wątek uzyskuje dostęp do obiektu.The calling thread waits while another thread accesses the object. Sygnały pulsu służą do powiadamiania oczekujących wątków o zmianach stanu obiektu.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (sygnał), PulseAllPulse (signal), PulseAll Wysyła sygnał do co najmniej jednego oczekującego wątku.Sends a signal to one or more waiting threads. Sygnał powiadamia wątek oczekujący, że stan zablokowanego obiektu został zmieniony, a właściciel blokady jest gotowy do zwolnienia blokady.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. Wątek oczekujący jest umieszczany w kolejce gotowości obiektu, tak aby mógł ostatecznie odebrać blokadę dla obiektu.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Gdy wątek ma blokadę, może sprawdzić nowy stan obiektu, aby sprawdzić, czy wymagany stan został osiągnięty.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 Zwalnia blokadę obiektu.Releases the lock on an object. Ta akcja oznacza również koniec sekcji krytycznej chronionej przez zablokowany obiekt.This action also marks the end of a critical section protected by the locked object.

Począwszy od Program .NET Framework 4.NET Framework 4istnieją dwa zestawy przeciążenia dla metod Enter i TryEnter.Beginning with the Program .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Jeden zestaw przeciążeń ma ref (in C#) lub ByRef (w Visual Basic) Boolean parametr, który jest niepodzielnie ustawiany do true, jeśli zostanie uzyskana blokada, nawet jeśli podczas uzyskiwania blokady wystąpi wyjątek.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. Użyj tych przeciążeń, jeśli ma krytyczne znaczenie dla zwolnienia blokady we wszystkich przypadkach, nawet jeśli zasoby, które są chronione przez blokadę, mogą nie być w spójnym stanie.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.

Obiekt blokadyThe lock object

Klasa monitora składa się z metod static C#(in) lub Shared (w Visual Basic), które działają na obiekcie, który kontroluje dostęp do sekcji krytycznej.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. Dla każdego synchronizowanego obiektu są utrzymywane następujące informacje:The following information is maintained for each synchronized object:

  • Odwołanie do wątku, w którym aktualnie znajduje się blokada.A reference to the thread that currently holds the lock.

  • Odwołanie do kolejki gotowości, która zawiera wątki gotowe do uzyskania blokady.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Odwołanie do oczekującej kolejki, która zawiera wątki czekające na powiadomienie zmiany stanu zablokowanego obiektu.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 blokuje obiekty (czyli typy odwołań), a nie typy wartości.Monitor locks objects (that is, reference types), not value types. Można przekazać typ wartości do Enter i Exit, który jest opakowany oddzielnie dla każdego wywołania.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Ponieważ każde wywołanie tworzy oddzielny obiekt, Enter nigdy nie bloki, a kod, który jest chroniony supposedly, nie jest w rzeczywistości synchronizowany.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. Ponadto obiekt przekazano do Exit różni się od obiektu przekazano do Enter, dlatego Monitor zgłasza wyjątek SynchronizationLockException z komunikatem "Metoda synchronizacji obiektów została wywołana z niezsynchronizowanego bloku kodu".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."

Poniższy przykład ilustruje ten problem.The following example illustrates this problem. Uruchamia dziesięć zadań, z których każdy stanie się uśpiony przez 250 milisekund.It launches ten tasks, each of which just sleeps for 250 milliseconds. Każde zadanie aktualizuje zmienną licznika, nTasks, która jest przeznaczona do policzania liczby zadań, które faktycznie zostały uruchomione i wykonane.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Ponieważ nTasks jest zmienną globalną, która może być aktualizowana jednocześnie przez wiele zadań, monitor służy do ochrony przed jednoczesnymi modyfikacjami przez wiele zadań.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. Jednak dane wyjściowe z przykładu pokazują, że każde zadanie zgłasza wyjątek 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.

Każde zadanie zgłasza wyjątek SynchronizationLockException, ponieważ zmienna nTasks jest opakowana przed wywołaniem metody Monitor.Enter w każdym zadaniu.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. Innymi słowy, każde wywołanie metody jest przekazanie oddzielnej zmiennej, która jest niezależna od innych.In other words, each method call is passed a separate variable that is independent of the others. nTasks jest ponownie opakowany w wywołaniu metody Monitor.Exit.nTasks is boxed again in the call to the Monitor.Exit method. Ponownie tworzy to dziesięć nowych zmiennych opakowanych, które są niezależne od siebie, nTasksi dziesięciu zmiennych opakowanych utworzonych w wywołaniu metody Monitor.Enter.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. Wyjątek jest zgłaszany, a następnie, ponieważ nasz kod próbuje zwolnić blokadę dla nowo utworzonej zmiennej, która nie została wcześniej zablokowana.The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

Chociaż można wpisać zmienną typu wartości przed wywołaniem Enter i Exit, jak pokazano w poniższym przykładzie, i przekazać ten sam obiekt opakowany do obu tych metod, nie ma możliwości wykonania tej czynności.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. Zmiany w zmiennej nieopakowanej nie są uwzględniane w kopii w ramce i nie ma sposobu zmiany wartości kopii w ramce.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.

Podczas wybierania obiektu do synchronizowania należy zablokować tylko w obiektach prywatnych i wewnętrznych.When selecting an object on which to synchronize, you should lock only on private or internal objects. Zablokowanie obiektów zewnętrznych może spowodować zakleszczenie, ponieważ niezwiązany kod może wybrać te same obiekty do zablokowania w różnych celach.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Należy pamiętać, że można synchronizować obiekt w wielu domenach aplikacji, jeśli obiekt używany do blokowania pochodzi z MarshalByRefObject.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

Sekcja krytycznaThe critical section

Użyj metod Enter i Exit do oznaczenia początku i końca sekcji krytycznej.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Uwaga

Funkcje zapewniane przez Enter i Exit są takie same jak w instrukcji Lock w C# i instrukcji SyncLock w Visual Basic, z tą różnicą, że konstrukcja języka otacza Przeciążenie metody Monitor.Enter(Object, Boolean) i metodę Monitor.Exit w 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 Blokuj, aby upewnić się, że monitor został opublikowany.block to ensure that the monitor is released.

Jeśli Sekcja krytyczna jest zestawem ciągłych instrukcji, blokada uzyskana przez metodę Enter gwarantuje, że tylko jeden wątek może wykonać załączony kod z zablokowanym obiektem.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. W tym przypadku zalecamy umieszczenie tego kodu w bloku try i umieszczenie wywołania metody Exit w bloku 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. Gwarantuje to, że blokada zostanie wydana, nawet jeśli wystąpi wyjątek.This ensures that the lock is released even if an exception occurs. Poniższy fragment kodu ilustruje ten wzorzec.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

Ta funkcja jest zwykle używana do synchronizowania dostępu do statycznej metody lub klasy wystąpień.This facility is typically used to synchronize access to a static or instance method of a class.

Jeśli Sekcja krytyczna obejmuje całą metodę, można uzyskać możliwość blokowania poprzez umieszczenie System.Runtime.CompilerServices.MethodImplAttribute na metodzie i określenie wartości Synchronized w konstruktorze System.Runtime.CompilerServices.MethodImplAttribute.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. W przypadku użycia tego atrybutu nie są potrzebne wywołania metody Enter i Exit.When you use this attribute, the Enter and Exit method calls are not needed. Poniższy fragment kodu ilustruje ten wzorzec:The following code fragment illustrates this pattern:

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

Należy zauważyć, że ten atrybut powoduje, że bieżący wątek utrzymuje blokadę do momentu, gdy metoda zwróci wartość; Jeśli blokada może zostać wydana wcześniej, użyj klasy Monitor, C# instrukcji Lock lub instrukcji Visual Basic SyncLock wewnątrz metody zamiast atrybutu.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.

Chociaż istnieje możliwość Enter i Exit instrukcji, które blokują i zwalniają dany obiekt do granic składowych lub klas lub obu tych zasad nie jest zalecane.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.

Puls, PulseAll i WaitPulse, PulseAll, and Wait

Gdy wątek jest właścicielem blokady i wprowadza sekcję krytyczną, która jest chroniona przez blokadę, może wywoływać metody Monitor.Wait, Monitor.Pulsei 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.

Gdy wątek, który przechowuje wywołania blokad Wait, blokada zostaje wydana i wątek zostanie dodany do oczekującej kolejki synchronizowanego obiektu.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. Pierwszy wątek w kolejce gotowości, jeśli istnieje, uzyskuje blokadę i wprowadza sekcję krytyczną.The first thread in the ready queue, if any, acquires the lock and enters the critical section. Wątek, który wywołał Wait, jest przenoszony z kolejki oczekujące do kolejki gotowości, gdy Monitor.Pulse lub metoda Monitor.PulseAll jest wywoływana przez wątek, który przechowuje blokadę (do przeniesienia, wątek musi znajdować się na końcu kolejki oczekiwania).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). Metoda Wait zwraca, gdy wątek wywołujący ponownie uzyskuje blokadę.The Wait method returns when the calling thread reacquires the lock.

Gdy wątek, który przechowuje wywołania blokad Pulse, wątek w nagłówku oczekującej kolejki zostanie przeniesiony do kolejki gotowości.When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. Wywołanie metody PulseAll przenosi wszystkie wątki z oczekującej kolejki do kolejki gotowości.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitory i uchwyty oczekiwaniaMonitors and wait handles

Ważne jest, aby zauważyć rozróżnienie między wykorzystaniem klasy Monitor i WaitHandle obiektów.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • Klasa Monitor jest całkowicie zarządzana, w pełni przenośna i może być bardziej wydajna w zakresie wymagań dotyczących zasobów systemu operacyjnego.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • obiekty WaitHandle reprezentują obiekty oczekiwane przez system operacyjny, są przydatne do synchronizowania kodu zarządzanego i niezarządzanego oraz uwidaczniają niektóre zaawansowane funkcje systemu operacyjnego, takie jak możliwość oczekiwania na wiele obiektów jednocześnie.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.

Właściwości

LockContentionCount

Pobiera informacje o tym, ile razy wystąpił rywalizacja podczas próby przeprowadzenia blokady monitora.Gets the number of times there was contention when trying to take the monitor's lock.

Metody

Enter(Object)

Uzyskuje blokadę na wyłączność dla określonego obiektu.Acquires an exclusive lock on the specified object.

Enter(Object, Boolean)

Uzyskuje blokadę na wyłączność dla określonego obiektu i niepodzielnie ustawia wartość wskazującą, czy blokada została wykonana.Acquires an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

Exit(Object)

Zwalnia blokadę na wyłączność dla określonego obiektu.Releases an exclusive lock on the specified object.

IsEntered(Object)

Określa, czy bieżący wątek przechowuje blokadę określonego obiektu.Determines whether the current thread holds the lock on the specified object.

Pulse(Object)

Powiadamia wątek w kolejce oczekujące zmiany stanu zablokowanego obiektu.Notifies a thread in the waiting queue of a change in the locked object's state.

PulseAll(Object)

Powiadamia wszystkie oczekujące wątki zmiany stanu obiektu.Notifies all waiting threads of a change in the object's state.

TryEnter(Object)

Próbuje uzyskać blokadę na wyłączność dla określonego obiektu.Attempts to acquire an exclusive lock on the specified object.

TryEnter(Object, Boolean)

Próbuje uzyskać wyłączną blokadę określonego obiektu i niepodzielnie ustawia wartość wskazującą, czy blokada została wykonana.Attempts to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

TryEnter(Object, Int32)

Próby dla określonej liczby milisekund w celu uzyskania blokady na wyłączność dla określonego obiektu.Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object.

TryEnter(Object, Int32, Boolean)

Próby, przez określoną liczbę milisekund, do uzyskania blokady na wyłączność określonego obiektu i niepodzielnie ustawia wartość wskazującą, czy blokada została wykonana.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)

Próby, przez określony czas, uzyskania blokady wyłącznej dla określonego obiektu.Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object.

TryEnter(Object, TimeSpan, Boolean)

Próbuje, przez określony czas, uzyskać blokadę na wyłączność określonego obiektu i niepodzielnie ustawia wartość wskazującą, czy blokada została wykonana.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.

Wait(Object)

Zwalnia blokadę obiektu i blokuje bieżący wątek do momentu odzyskania blokady.Releases the lock on an object and blocks the current thread until it reacquires the lock.

Wait(Object, Int32)

Zwalnia blokadę obiektu i blokuje bieżący wątek do momentu odzyskania blokady.Releases the lock on an object and blocks the current thread until it reacquires the lock. Jeśli upłynie określony interwał limitu czasu, wątek przechodzi do kolejki gotowości.If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, Int32, Boolean)

Zwalnia blokadę obiektu i blokuje bieżący wątek do momentu odzyskania blokady.Releases the lock on an object and blocks the current thread until it reacquires the lock. Jeśli upłynie określony interwał limitu czasu, wątek przechodzi do kolejki gotowości.If the specified time-out interval elapses, the thread enters the ready queue. Ta metoda określa również, czy domena synchronizacji kontekstu (Jeśli w synchronizowanym kontekście) zostanie zakończona przed zaczekaniem i ponownym pozyskaniem.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, TimeSpan)

Zwalnia blokadę obiektu i blokuje bieżący wątek do momentu odzyskania blokady.Releases the lock on an object and blocks the current thread until it reacquires the lock. Jeśli upłynie określony interwał limitu czasu, wątek przechodzi do kolejki gotowości.If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, TimeSpan, Boolean)

Zwalnia blokadę obiektu i blokuje bieżący wątek do momentu odzyskania blokady.Releases the lock on an object and blocks the current thread until it reacquires the lock. Jeśli upłynie określony interwał limitu czasu, wątek przechodzi do kolejki gotowości.If the specified time-out interval elapses, the thread enters the ready queue. Opcjonalnie zamyka domenę synchronizacji dla kontekstu synchronizowanego przed oczekiwaniem i ponownie uzyskuje domenę.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

Dotyczy

Bezpieczeństwo wątkowe

Ten typ jest bezpieczny wątkowo.This type is thread safe.

Zobacz też