Monitor Monitor Monitor Monitor Class

Définition

Fournit un mécanisme qui synchronise l'accès aux objets.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
Héritage
MonitorMonitorMonitorMonitor
Attributs

Exemples

L’exemple suivant utilise la Monitor classe pour synchroniser l’accès à une instance unique d’un générateur de nombres aléatoires représenté par la Random classe.The following example uses the Monitor class to synchronize access to a single instance of a random number generator represented by the Random class. L’exemple crée dix tâches, chacune s’exécutant de façon asynchrone sur un thread de pool de threads.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Chaque tâche génère des nombres aléatoires de 10 000, calcule leur moyenne et met à jour deux variables de niveau procédure qui maintiennent un total cumulé du nombre de nombres aléatoires générés et leur somme.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. Une fois que toutes les tâches ont été exécutées, ces deux valeurs sont ensuite utilisées pour calculer la moyenne globale.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)

Étant donné qu’ils sont accessibles à partir de n’importe quelle tâche exécutée sur un thread de total pool n de threads, l’accès aux variables et doit également être synchronisé.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. La Interlocked.Add méthode est utilisée à cet effet.The Interlocked.Add method is used for this purpose.

L’exemple suivant illustre l’utilisation combinée de la Monitor classe (implémentée avec lock la SyncLock construction de langage ou) Interlocked , de la classe AutoResetEvent et de la classe.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. Il définit deux classes internal (en C#) ou Friend (en Visual Basic), SyncResource et UnSyncResource, qui fournissent respectivement un accès synchronisé et non synchronisé à une ressource.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. Pour garantir que l’exemple illustre la différence entre l’accès synchronisé et non synchronisé (ce qui pourrait être le cas si chaque appel de méthode se termine rapidement), la méthode inclut un délai aléatoire : pour les threads dont la propriété Thread.ManagedThreadId est paire, la méthode appelle Thread.Sleep pour introduire un délai de 2000 millisecondes.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. La classe SyncResource n’étant pas publique, aucune partie du code client n’acquiert un verrou sur la ressource synchronisée. C’est la classe interne proprement dite qui acquiert le verrou.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. Cela empêche que du code malveillant acquière un verrou sur un objet public.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.

L’exemple définit une variable, numOps, qui définit le nombre de threads qui tenteront d’accéder à la ressource.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. Le thread d’application appelle la méthode ThreadPool.QueueUserWorkItem(WaitCallback) à cinq reprises pour l’accès synchronisé et non synchronisé.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. La méthode ThreadPool.QueueUserWorkItem(WaitCallback) possède un seul paramètre, un délégué qui n’accepte aucun paramètre et ne retourne aucune valeur.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. Pour l’accès synchronisé, elle appelle la méthode SyncUpdateResource. Pour l’accès non synchronisé, elle appelle la méthode UnSyncUpdateResource.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Après chaque ensemble d’appels de méthode, le thread d’application appelle la méthode AutoResetEvent. WaitOne afin qu’il se AutoResetEvent bloque jusqu’à ce que l’instance soit signalée.After each set of method calls, the application thread calls the AutoResetEvent.WaitOne method so that it blocks until the AutoResetEvent instance is signaled.

Chaque appel à la méthode SyncUpdateResource appelle la méthode SyncResource.Access interne, puis appelle la méthode Interlocked.Decrement pour décrémenter le compteur 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. La Interlocked.Decrement méthode est utilisée pour décrémenter le compteur, car dans le cas contraire, vous ne pouvez pas être certain qu’un deuxième thread accédera à la valeur avant que la valeur décrémentée d’un premier thread soit stockée dans la variable.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. Lorsque le dernier thread de travail synchronisé décrémente le compteur à zéro, ce qui indique que tous les threads synchronisés ont terminé d’accéder à SyncUpdateResource la ressource, EventWaitHandle.Set la méthode appelle la méthode, qui indique au thread principal de continuer. production.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.

Chaque appel à la méthode UnSyncUpdateResource appelle la méthode UnSyncResource.Access interne, puis appelle la méthode Interlocked.Decrement pour décrémenter le compteur 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. Une fois encore, Interlocked.Decrement la méthode est utilisée pour décrémenter le compteur afin de garantir qu’un deuxième thread n’accède pas à la valeur avant qu’une valeur décrémentée du premier thread n’ait été assignée à la variable.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. Lorsque le dernier thread de travail non synchronisé décrémente le compteur à zéro, ce qui indique qu’aucun thread plus non synchronisé n’a besoin d’accéder à la UnSyncUpdateResource ressource, la EventWaitHandle.Set méthode appelle la méthode, qui indique au thread principal de continuer l’exécution. .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.

Comme le montre la sortie de l’exemple, l’accès synchronisé garantit que le thread appelant quitte la ressource protégée avant qu’un autre thread puisse y accéder ; chaque thread attend son prédécesseur.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. En revanche, sans verrou la méthode UnSyncResource.Access est appelée dans l’ordre dans lequel les threads l’atteignent.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Remarques

La Monitor classe vous permet de synchroniser l’accès à une région de code en adoptant et en libérant un verrou sur un Monitor.Enterobjet particulier en Monitor.Exit appelant les méthodes, Monitor.TryEnteret.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. Les verrous d’objets permettent de restreindre l’accès à un bloc de code, communément appelé section critique.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. Tandis qu’un thread possède le verrou d’un objet, aucun autre thread ne peut acquérir ce verrou.While a thread owns the lock for an object, no other thread can acquire that lock. Vous pouvez également utiliser la Monitor classe pour vous assurer qu’aucun autre thread n’est autorisé à accéder à une section du code d’application en cours d’exécution par le propriétaire du verrou, à moins que l’autre thread exécute le code à l’aide d’un objet verrouillé différent.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.

Dans cet article :In this article:

La classe Monitor : Vue d’ensemble The Monitor class: An overview
Objet Lock The lock object
La section critique The critical section
Pulse, PulseAll et Wait Pulse, PulseAll, and Wait
Moniteurs et les handles d’attenteMonitors and wait handles

La classe Monitor : Vue d’ensembleThe Monitor class: An overview

Monitor présente les caractéristiques suivantes :Monitor has the following features:

  • Il est associé à un objet à la demande.It is associated with an object on demand.

  • Il est indépendant, ce qui signifie qu’il peut être appelé directement à partir de n’importe quel contexte.It is unbound, which means it can be called directly from any context.

  • Une instance de la Monitor classe ne peut pas être créée ; les méthodes Monitor de la classe sont toutes statiques.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. Chaque méthode reçoit l’objet synchronisé qui contrôle l’accès à la section critique.Each method is passed the synchronized object that controls access to the critical section.

Notes

Utilisez la Monitor classe pour verrouiller des objets autres que des chaînes (autrement dit, des types Stringréférence autres que), et non des types valeur.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Pour plus d’informations, consultez les surcharges Enter de la méthode et la section objet Lock plus loin dans cet article.For details, see the overloads of the Enter method and The lock object section later in this article.

Le tableau suivant décrit les actions qui peuvent être effectuées par les threads qui accèdent aux objets synchronisés :The following table describes the actions that can be taken by threads that access synchronized objects:

ActionAction DescriptionDescription
Enter, TryEnterEnter, TryEnter Acquiert un verrou pour un objet.Acquires a lock for an object. Cette action marque également le début d’une section critique.This action also marks the beginning of a critical section. Aucun autre thread ne peut entrer dans la section critique à moins d’exécuter les instructions de la section critique à l’aide d’un objet verrouillé différent.No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait Libère le verrou sur un objet afin d’autoriser d’autres threads à verrouiller et à accéder à l’objet.Releases the lock on an object in order to permit other threads to lock and access the object. Le thread appelant attend qu’un autre thread accède à l’objet.The calling thread waits while another thread accesses the object. Les signaux d’impulsions sont utilisés pour notifier les threads en attente des modifications apportées à l’état d’un objet.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (signal), PulseAllPulse (signal), PulseAll Envoie un signal à un ou plusieurs threads en attente.Sends a signal to one or more waiting threads. Le signal avertit un thread en attente que l’état de l’objet verrouillé a changé et que le propriétaire du verrou est prêt à libérer le verrou.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. Le thread en attente est placé dans la file d’attente opérationnelle de l’objet afin qu’il puisse éventuellement recevoir le verrou de l’objet.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Une fois que le thread a le verrou, il peut vérifier le nouvel état de l’objet pour voir si l’État requis a été atteint.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 Libère le verrou sur un objet.Releases the lock on an object. Cette action marque également la fin d’une section critique protégée par l’objet verrouillé.This action also marks the end of a critical section protected by the locked object.

À partir de Enter TryEnter , il existe deux ensembles de surcharges pour les .NET Framework 4.NET Framework 4méthodes et.Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Un ensemble de surcharges a un ref paramètre ( C#dans) ByRef ou (dans Visual Basic Boolean ) qui est défini de manière atomique true sur si le verrou est acquis, même si une exception est levée lors de l’acquisition du verrou.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. Utilisez ces surcharges s’il est essentiel de libérer le verrou dans tous les cas, même si les ressources que le verrou protège ne sont pas dans un état cohérent.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.

Objet LockThe lock object

La classe Monitor se compose static des méthodes C#(in Shared ) ou (dans Visual Basic) qui opèrent sur un objet qui contrôle l’accès à la section critique.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. Les informations suivantes sont conservées pour chaque objet synchronisé :The following information is maintained for each synchronized object:

  • Référence au thread qui détient actuellement le verrou.A reference to the thread that currently holds the lock.

  • Référence à une file d’attente opérationnelle, qui contient les threads prêts à obtenir le verrou.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Référence à une file d’attente en attente, qui contient les threads en attente de notification d’une modification de l’état de l’objet verrouillé.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 verrouille des objets (c'est-à-dire des types référence), mais pas des types valeur.Monitor locks objects (that is, reference types), not value types. Il est possible de passer un type valeur à Enter et à Exit, mais il est converti (boxed) séparément pour chaque appel.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Étant donné que chaque appel crée un objet distinct, Enter n'est jamais bloqué, et le code qu'il est censé protéger n'est pas correctement synchronisé.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. Comme l'objet passé à Exit est en plus différent de l'objet passé à Enter, Monitor lève l'exception SynchronizationLockException avec le message suivant : « La méthode de synchronisation de l'objet a été appelée à partir d'un bloc de code non synchronisé ».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."

L'exemple de code suivant illustre ce problème.The following example illustrates this problem. Il lance dix tâches, chacune d'elles restant en veille pendant 250 millisecondes seulement.It launches ten tasks, each of which just sleeps for 250 milliseconds. Ensuite, chaque tâche met à jour une variable de compteur, nTasks, qui sert à compter le nombre de tâches ayant été lancées et exécutées.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. nTasks est une variable globale qui peut être modifiée par plusieurs tâches simultanément. Pour empêcher cela, un gestionnaire (monitor) est utilisé.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. Toutefois, chaque tâche lève une exception SynchronizationLockException, comme le montre le résultat de l'exemple.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.

Chaque tâche lève une exception SynchronizationLockException, car la variable nTasks est convertie (boxed) avant l'appel à la méthode Monitor.Enter dans chaque tâche.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. En d'autres termes, chaque appel de méthode est passé à une variable distincte, qui est indépendante des autres variables.In other words, each method call is passed a separate variable that is independent of the others. nTasks est de nouveau convertie (boxed) dans l'appel à la méthode Monitor.Exit.nTasks is boxed again in the call to the Monitor.Exit method. Cette opération crée encore dix variables boxed qui sont indépendantes les unes des autres, nTasks, et les dix variables boxed dans l'appel à la méthode 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. L'exception est levée, car le code tente de libérer un verrou sur une nouvelle variable qui n'était pas précédemment verrouillée.The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

Vous pouvez convertir (box) une variable de type valeur avant d'appeler Enter et Exit, comme dans l'exemple suivant, et passer le même objet boxed aux deux méthodes, mais cette opération n'offre aucun avantage.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. En effet, les modifications apportées à la variable non convertie (unboxed) ne sont pas répercutées dans la copie convertie (boxed), et il n'est pas possible de modifier la valeur de cette copie.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.

Lorsque vous sélectionnez un objet sur lequel synchroniser, vous devez verrouiller uniquement les objets privés ou internes.When selecting an object on which to synchronize, you should lock only on private or internal objects. Le verrouillage sur des objets externes peut entraîner des interblocages, car un code non lié peut choisir les mêmes objets à verrouiller à des fins différentes.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Notez que vous pouvez synchroniser sur un objet dans plusieurs domaines d’application si l’objet utilisé pour le verrou dérive de MarshalByRefObject.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

Section critiqueThe critical section

Utilisez les Enter méthodes Exit et pour marquer le début et la fin d’une section critique.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Notes

Les fonctionnalités fournies par les Enter méthodes Exit et sont identiques à celles fournies par l’instruction Lock dans C# et l’instruction SyncLock dans Visual Basic, sauf que les constructions de langage encapsulent le Monitor.Enter(Object, Boolean)la surcharge de méthode Monitor.Exit et la méthode trydans un...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 bloquer pour vous assurer que le moniteur est relâché.block to ensure that the monitor is released.

Si la section critique est un ensemble d’instructions contiguës, le verrou acquis par la Enter méthode garantit qu’un seul thread peut exécuter le code délimité avec l’objet verrouillé.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. Dans ce cas, nous vous recommandons de placer ce code dans un try bloc et de placer l’appel à Exit la méthode dans finally un bloc.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. Cela garantit la libération du verrou même si une exception se produit.This ensures that the lock is released even if an exception occurs. Le fragment de code suivant illustre ce modèle.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

Cette fonctionnalité est généralement utilisée pour synchroniser l’accès à une méthode d’instance ou statique d’une classe.This facility is typically used to synchronize access to a static or instance method of a class.

Si une section critique couvre une méthode entière, la fonctionnalité de verrouillage peut être obtenue en plaçant le System.Runtime.CompilerServices.MethodImplAttribute sur la méthode et en spécifiant Synchronized la valeur dans le constructeur System.Runtime.CompilerServices.MethodImplAttributede.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. Lorsque vous utilisez cet attribut, les Enter appels Exit de méthode et ne sont pas nécessaires.When you use this attribute, the Enter and Exit method calls are not needed. Le fragment de code suivant illustre ce modèle :The following code fragment illustrates this pattern:

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

Notez que l’attribut fait que le thread actuel conserve le verrou jusqu’à ce que la méthode soit retournée ; Si le verrou peut être libéré plus tôt, utilisez Monitor la classe, C# l’instruction Lock ou l’instruction Visual Basic SyncLock à l’intérieur de la méthode au lieu de l’attribut.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.

Bien qu’il soit possible pour Enter les Exit instructions et qui verrouillent et libèrent un objet donné pour franchir les limites d’un membre ou d’une classe, ou les deux, cette pratique n’est pas recommandée.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, PulseAll et WaitPulse, PulseAll, and Wait

Une fois qu’un thread possède le verrou et qu’il a entré la section critique protégée par le verrou, Monitor.Waitil Monitor.Pulsepeut appeler Monitor.PulseAll les méthodes, et.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.

Lorsque le thread qui contient le verrou appelle Wait, le verrou est libéré et le thread est ajouté à la file d’attente en attente de l’objet synchronisé.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. Le premier thread de la file d’attente opérationnelle, le cas échéant, acquiert le verrou et entre dans la section critique.The first thread in the ready queue, if any, acquires the lock and enters the critical section. Le thread qui a Wait appelé est déplacé de la file d’attente en attente vers la file Monitor.Pulse d’attente Monitor.PulseAll opérationnelle lorsque la méthode ou est appelée par le thread qui détient le verrou (à déplacer, le thread doit se trouver au début de la file d’attente en attente).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). La Wait méthode retourne lorsque le thread appelant réacquière le verrou.The Wait method returns when the calling thread reacquires the lock.

Lorsque le thread qui contient le verrou appelle Pulse, le thread situé à l’en-tête de la file d’attente en attente est déplacé vers la file d’attente opérationnelle.When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. L’appel à la PulseAll méthode déplace tous les threads de la file d’attente en attente vers la file d’attente opérationnelle.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Analyses et handles d’attenteMonitors and wait handles

Il est important de noter la différence entre l’utilisation de la Monitor classe et WaitHandle les objets.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • La Monitor classe est purement managée, entièrement portable et peut être plus efficace en termes de besoins en ressources du système d’exploitation.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • Les objets WaitHandle représentent des objets d'attente de système d'exploitation et sont utiles pour la synchronisation entre le code managé et le code non managé. Ils exposent certaines fonctionnalités avancées de système d'exploitation, comme la possibilité d'attendre plusieurs objets à la fois.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.

Propriétés

LockContentionCount LockContentionCount LockContentionCount LockContentionCount

Obtient le nombre de fois où il y a eu de la contention lors des tentatives de prendre le verrou du moniteur.Gets the number of times there was contention when trying to take the monitor's lock.

Méthodes

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

Acquiert un verrou exclusif sur l'objet spécifié.Acquires an exclusive lock on the specified object.

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

Acquiert un verrou exclusif sur l'objet spécifié et définit de manière atomique une valeur qui indique si le verrou a été pris.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)

Libère un verrou exclusif sur l'objet spécifié.Releases an exclusive lock on the specified object.

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

Détermine si le thread actuel détient le verrou sur l'objet spécifié.Determines whether the current thread holds the lock on the specified object.

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

Avertit un thread situé dans la file d'attente en suspens d'un changement d'état de l'objet verrouillé.Notifies a thread in the waiting queue of a change in the locked object's state.

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

Avertit tous les threads en attente d'un changement d'état de l'objet.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)

Tente, pendant le délai spécifié, d'acquérir un verrou exclusif sur l'objet spécifié et définit de manière atomique une valeur qui indique si le verrou a été pris.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)

Tente, pendant le nombre spécifié de millisecondes, d'acquérir un verrou exclusif sur l'objet spécifié et définit de manière atomique une valeur qui indique si le verrou a été pris.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)

Tentatives d'acquisition d'un verrou exclusif sur l'objet spécifié au cours de la période spécifiée.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)

Tente d'acquérir un verrou exclusif sur l'objet spécifié et définit de manière atomique une valeur qui indique si le verrou a été pris.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)

Essaie d'acquérir un verrou exclusif sur l'objet spécifié.Attempts to acquire an exclusive lock on the specified object.

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

Tentatives d'acquisition d'un verrou exclusif sur l'objet spécifié au cours du nombre spécifié de millisecondes.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)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.If the specified time-out interval elapses, the thread enters the ready queue. Cette méthode spécifie également si le domaine de synchronisation associé au contexte (dans le cas d'un contexte synchronisé) est abandonné avant l'attente et acquis à nouveau par la suite.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)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou.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)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.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)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.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)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.If the specified time-out interval elapses, the thread enters the ready queue. Le domaine de synchronisation associé au contexte synchronisé peut être abandonné avant l’attente et acquis de nouveau par la suite.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

S’applique à

Cohérence de thread

Ce type est thread-safe.This type is thread safe.

Voir aussi