Cet article a fait l’objet d’une traduction automatique. Pour afficher l’article en anglais, activez la case d’option Anglais. Vous pouvez également afficher le texte anglais dans une fenêtre contextuelle en faisant glisser le pointeur de la souris sur le texte traduit.
Traduction
Anglais

Monitor classe

 

Fournit un mécanisme qui synchronise l'accès aux objets.

Espace de noms:   System.Threading
Assembly:  mscorlib (dans mscorlib.dll)

System.Object
  System.Threading.Monitor

[ComVisibleAttribute(true)]
[HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, 
	ExternalThreading = true)]
public static class Monitor

NomDescription
System_CAPS_pubmethodSystem_CAPS_staticEnter(Object)

Acquiert un verrou exclusif sur l'objet spécifié.

System_CAPS_pubmethodSystem_CAPS_staticEnter(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.

System_CAPS_pubmethodSystem_CAPS_staticExit(Object)

Libère un verrou exclusif sur l’objet spécifié.

System_CAPS_pubmethodSystem_CAPS_staticIsEntered(Object)

Détermine si le thread actuel détient le verrou sur l'objet spécifié.

System_CAPS_pubmethodSystem_CAPS_staticPulse(Object)

Avertit un thread situé dans la file d'attente en suspens d'un changement d'état de l'objet verrouillé.

System_CAPS_pubmethodSystem_CAPS_staticPulseAll(Object)

Avertit tous les threads en attente d'un changement d'état de l'objet.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(Object)

Essaie d'acquérir un verrou exclusif sur l'objet spécifié.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(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.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(Object, Int32)

Tentatives d'acquisition d'un verrou exclusif sur l'objet spécifié au cours du nombre spécifié de millisecondes.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(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.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(Object, TimeSpan)

Tentatives d'acquisition d'un verrou exclusif sur l'objet spécifié au cours de la période spécifiée.

System_CAPS_pubmethodSystem_CAPS_staticTryEnter(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.

System_CAPS_pubmethodSystem_CAPS_staticWait(Object)

Libère le verrou d'un objet et bloque le thread actuel jusqu'à ce qu'il acquière à nouveau le verrou.

System_CAPS_pubmethodSystem_CAPS_staticWait(Object, Int32)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.

System_CAPS_pubmethodSystem_CAPS_staticWait(Object, Int32, Boolean)

Libère le verrou d'un objet et bloque le thread actuel jusqu'à ce qu'il acquière à nouveau le verrou. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle. 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.

System_CAPS_pubmethodSystem_CAPS_staticWait(Object, TimeSpan)

Libère le verrou d'un objet et bloque le thread actuel jusqu'à ce qu'il acquière à nouveau le verrou. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle.

System_CAPS_pubmethodSystem_CAPS_staticWait(Object, TimeSpan, Boolean)

Libère le verrou d’un objet et bloque le thread actuel jusqu’à ce qu’il acquière à nouveau le verrou. Si le délai d'attente spécifié est écoulé, le thread intègre la file d'attente opérationnelle. Le domaine de synchronisation associé au contexte synchronisé peut être abandonné avant l’attente et acquis de nouveau par la suite.

Le Monitor classe vous permet de synchroniser l’accès à une région de code en prenant et en libérant un verrou sur un objet particulier en appelant le Monitor.Enter, Monitor.TryEnter, et Monitor.Exit méthodes. Les verrous d’objets permettent de restreindre l’accès à un bloc de code, communément appelé une section critique. Lors d’un thread possède le verrou d’un objet, aucun autre thread ne peut acquérir ce verrou. Vous pouvez également utiliser le Monitor pour vous assurer qu’aucun autre thread est autorisé à accéder à une section de l’application code de la classe en cours d’exécution par le propriétaire du verrou, sauf si l’autre thread exécute le code à l’aide d’un objet verrouillé différent.

Dans cet article :

La classe Monitor : une vue d’ensemble
L’objet de verrouillage
La section critique
Attente Pulse et PulseAll
Analyses et les handles d’attente

Monitorprésente les caractéristiques suivantes :

  • Il est associé à un objet à la demande.

  • Il est indépendant, ce qui signifie qu’il peut être appelée directement à partir de n’importe quel contexte.

  • Une instance de la Monitor classe ne peut pas être créée ; les méthodes de la Monitor classe sont tous statiques. Chaque méthode est passé à l’objet de synchronisation qui contrôle l’accès à la section critique.

System_CAPS_noteRemarque

Utilisez le Monitor classe pour verrouiller des objets autres que des chaînes (autrement dit, les types référence autres que String), pas des types valeur. Pour plus d’informations, consultez les surcharges de la Enter méthode et L’objet de verrouillage section plus loin dans cet article.

Le tableau suivant décrit les actions pouvant être consommées par les threads qui accèdent à des objets synchronisés :

Action

Description

Enter, TryEnter

Acquiert un verrou d’un objet. Cette action marque également le début d’une section critique. Aucun autre thread ne peut entrer la section critique, sauf si elle exécute les instructions dans la section critique à l’aide d’un objet verrouillé différent.

Wait

Libère le verrou sur un objet afin d’autoriser d’autres threads pour verrouiller et accéder à l’objet. Le thread appelant attend qu’un autre thread accède à l’objet. Signaux d’impulsion sont utilisés pour signaler des threads en attente sur les modifications apportées à l’état d’un objet.

Pulse(signal),PulseAll

Envoie un signal à un ou plusieurs threads en attente. Ce signal avertit un thread en attente que l’état de l’objet verrouillé a changé, et le propriétaire du verrou est prêt à libérer le verrou. Le thread en attente est placé dans la file d’attente opérationnelle de l’objet afin qu’il peut recevoir le verrou pour l’objet. 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.

Exit

Libère le verrou sur un objet. Cette action marque également la fin d’une section critique protégée par l’objet verrouillé.

Compter les .NET Framework 4, il existe deux jeux de surcharges pour la Enter et TryEnter méthodes. Un ensemble de surcharges a un ref (en c#) ou ByRef (en Visual Basic) Boolean paramètre qui est automatiquement défini sur true si le verrou est acquis, même si une exception est levée lors de l’acquisition du verrou. Utilisez ces surcharges s’il est essentiel pour libérer le verrou dans tous les cas, même lorsque les ressources que protège le verrou ne peuvent pas être dans un état cohérent. 

La classe Monitor se compose de static (en c#) ou Shared (en Visual Basic) les méthodes qui fonctionnent sur un objet qui contrôle l’accès à la section critique. Les informations suivantes sont conservées pour chaque objet synchronisé :

  • Une référence au thread qui détient actuellement le verrou.

  • Une référence à une file d’attente opérationnelle, qui contient les threads prêts à obtenir le verrou.

  • Une référence à une file d’attente, qui contient les threads en attente de notification de modification dans l’état de l’objet verrouillé.

Monitor verrouille des objets (c'est-à-dire des types référence), mais pas des types valeur. Il est possible de passer un type valeur à Enter et à Exit, mais il est converti (boxed) séparément pour chaque appel. É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é. 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é ».

L'exemple de code suivant illustre ce problème. Il lance dix tâches, chacune d'elles restant en veille pendant 250 millisecondes seulement. 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. 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é. Toutefois, chaque tâche lève une exception SynchronizationLockException, comme le montre le résultat de l'exemple.

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.

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. En d'autres termes, chaque appel de méthode est passé à une variable distincte, qui est indépendante des autres variables. nTasks est de nouveau convertie (boxed) dans l'appel à la méthode Monitor.Exit. 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. 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.

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. 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.

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.

Lorsque vous sélectionnez un objet sur lequel effectuer la synchronisation, vous devez verrouiller uniquement sur les objets privés ou internes. Verrouillage d’objets externes peut entraîner des blocages, étant donné que le code non lié peut choisir les mêmes objets à verrouiller à des fins différentes.

Notez que vous pouvez synchroniser sur un objet dans plusieurs domaines d’application si l’objet utilisé comme verrou dérive MarshalByRefObject.

Utilisez le Enter et Exit méthodes pour marquer le début et la fin d’une section critique.

System_CAPS_noteRemarque

Les fonctionnalités fournies par le Enter et Exit méthodes est identique à celle fournie par le verrou instruction en c# et la SyncLock instruction en Visual Basic, sauf que les constructions de type wrap le Monitor.Enter(Object, Boolean) surcharge de méthode et la Monitor.Exit méthode dans un try... finally bloc pour garantir que le moniteur est libéré.

Si la section critique est un jeu d’instructions contiguës, puis le verrou acquis par la Enter méthode ne garantit qu’un seul thread peut exécuter le code avec l’objet verrouillé. Dans ce cas, nous vous recommandons de placer ce code dans un try bloquer et de placer l’appel de la Exit méthode dans un finally bloc. Cela garantit la libération du verrou même si une exception se produit. Le fragment de code suivant illustre ce modèle.

// 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);
}

Cette fonctionnalité est généralement utilisée pour synchroniser l’accès à un mappage statique ou méthode d’instance d’une classe.

Si une section critique s’étend sur la totalité de la méthode, l’outil de verrouillage peut être obtenue en plaçant le System.Runtime.CompilerServices.MethodImplAttribute sur la méthode et en spécifiant le Synchronized valeur dans le constructeur de System.Runtime.CompilerServices.MethodImplAttribute. Lorsque vous utilisez cet attribut, le Enter et Exit les appels de méthode ne sont pas nécessaires. Le fragment de code suivant illustre ce modèle :

[MethodImplAttribute(MethodImplOptions.Synchronized)]
void MethodToLock()
{
   // Method implementation.
} 

Notez que l’attribut oblige le thread actuel détient le verrou jusqu'à ce que la méthode est retournée ; Si le verrou peut être libéré plus tôt, utilisez la Monitor classe c# verrou instruction ou Visual Basic SyncLock instruction à l’intérieur de la méthode au lieu de l’attribut.

S’il est possible pour le Enter et Exit les instructions qui verrouillent et libèrent un objet donné à l’intersection des membres ou des limites de classes ou les deux, cette pratique n’est pas recommandée.

Une fois qu’un thread possède le verrou et a entré le verrou protège la section critique, elle peut appeler le Monitor.Wait, Monitor.Pulse, et Monitor.PulseAll méthodes.

WaitLibère le verrou est maintenu, permet à un thread en attente ou threads pour obtenir le verrou et entrer dans la section critique et attend d’être averti par un appel à la Monitor.Pulse ou Monitor.PulseAll (méthode). Quand la méthode Wait reçoit la notification, elle retourne le verrou, puis le reprend.

Pulse et PulseAll envoient le signal d'exécution du thread suivant dans la file d'attente.

Il est important de noter la distinction entre l’utilisation de la Monitor classe et WaitHandle objets.

  • La Monitor classe est purement managés et entièrement portables et peut être plus efficace en termes de besoins en ressources de système d’exploitation.

  • 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.

L’exemple suivant utilise le Monitor classe pour synchroniser l’accès à une seule instance d’un générateur de nombres aléatoire représenté par la Random classe. L’exemple crée dix tâches, chacun d’eux exécute de façon asynchrone sur un thread de pool de threads. Chaque tâche génère des nombres aléatoires 10 000, calcule leur moyenne et met à jour les deux variables de niveau de la procédure permettant de maintenir le total en cours d’exécution, le nombre de nombres aléatoires et leur somme. Une fois que toutes les tâches ont été exécutées, ces deux valeurs sont ensuite utilisés pour calculer la moyenne globale.

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)

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. The M:System.Threading.Interlocked.Add(System.Int64@,System.Int64) method is used for this purpose.

L’exemple suivant illustre l’utilisation combinée de la Monitor classe (implémentée avec les lock ou SyncLock construction de langage), le Interlocked (classe) et la AutoResetEvent classe. 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. 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. 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. Cela empêche que du code malveillant acquière un verrou sur un objet public.

using System;
using System.Threading;

internal class SyncResource
{
    // Use a monitor to enforce synchronization.
    public void Access()
    {
        lock(this) {
            Console.WriteLine("Staring 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 esource 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:
//    Staring synchronized resource access on thread #6
//    Stopping synchronized resource access on thread #6
//    Staring synchronized resource access on thread #7
//    Stopping synchronized resource access on thread #7
//    Staring synchronized resource access on thread #3
//    Stopping synchronized resource access on thread #3
//    Staring synchronized resource access on thread #4
//    Stopping synchronized resource access on thread #4
//    Staring synchronized resource access on thread #5
//    Stopping synchronized resource access on thread #5
//
//    All synchronized operations have completed.
//
//    Starting unsynchronized esource access on Thread #7
//    Starting unsynchronized esource access on Thread #9
//    Starting unsynchronized esource access on Thread #10
//    Starting unsynchronized esource access on Thread #6
//    Starting unsynchronized esource 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.

The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. The application thread calls the M:System.Threading.ThreadPool.QueueUserWorkItem(System.Threading.WaitCallback) method for synchronized and unsynchronized access five times each. The M:System.Threading.ThreadPool.QueueUserWorkItem(System.Threading.WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. After each set of method calls, the application thread calls the AutoResetEvent.WaitOnehttps://msdn.microsoft.com/library/58195swd.aspx method so that it blocks until the T:System.Threading.AutoResetEvent instance is signaled.

Each call to the SyncUpdateResource method calls the internal SyncResource.Access method and then calls the M:System.Threading.Interlocked.Decrement(System.Int64@) method to decrement the numOps counter. The M:System.Threading.Interlocked.Decrement(System.Int64@) 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. 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.Sethttps://msdn.microsoft.com/library/system.threading.eventwaithandle.set.aspx method, which signals the main thread to continue execution.

Each call to the UnSyncUpdateResource method calls the internal UnSyncResource.Access method and then calls the M:System.Threading.Interlocked.Decrement(System.Int64@) method to decrement the numOps counter. Once again, the M:System.Threading.Interlocked.Decrement(System.Int64@) 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. 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.Sethttps://msdn.microsoft.com/library/system.threading.eventwaithandle.set.aspx method, which signals the main thread to continue execution.

As the output from the example shows, synchronized access ensures that the calling thread exits the protected resource before another thread can access it; each thread waits on its predecessor. On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Plateforme Windows universelle
Disponible depuis 8
.NET Framework
Disponible depuis 1.1
Bibliothèque de classes portable
Pris en charge dans : plateformes .NET portables
Silverlight
Disponible depuis 2.0
Silverlight pour Windows Phone
Disponible depuis 7.0
Windows Phone
Disponible depuis 8.1

Ce type est thread-safe.

Retour au début
Afficher: