Moniteurs

Les objets Monitor exposent la possibilité de synchroniser l'accès à une région de code en prenant et en libérant un verrou sur un objet particulier à l'aide des méthodes Monitor.Enter, Monitor.TryEnter et Monitor.Exit. Une fois qu'un verrou est placé sur une région de code, vous pouvez utiliser les méthodes Monitor.Wait, Monitor.Pulse et Monitor.PulseAll. Wait libère le verrou si celui-ci est maintenu et attend une notification. Lorsque Wait est notifié, il retourne et obtient à nouveau le verrou. Pulse et PulseAll indiquent au thread suivant dans la file d'attente de continuer.

Monitor verrouille des objets (c'est-à-dire des types référence), pas des types valeur. Bien que vous puissiez passer un type valeur à Enter et à Exit, celui-ci est converti séparément pour chaque appel. Comme chaque appel crée un objet séparé, Enter ne bloque jamais, et le code qu'il est censé protégé n'est pas vraiment synchronisé. De plus, l'objet passé à Exit est différent de l'objet passé à Enter, Monitor lève donc SynchronizationLockException avec le message « 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 ces problèmes.

Private x As Integer
' The next line creates a generic object containing the value of 
' x each time the code is executed, so that Enter never blocks.
Monitor.Enter(x)
Try
    ' Code that needs to be protected by the monitor.
Finally
    ' Always use Finally to ensure that you exit the Monitor.
    ' The following line creates another object containing 
    ' the value of x, and throws SynchronizationLockException
    ' because the two objects do not match.
    Monitor.Exit(x)
End Try
private int x;
// The next line creates a generic object containing the value of
// x each time the code is executed, so that Enter never blocks.
Monitor.Enter(x);
try {
    // Code that needs to be protected by the monitor.
}
finally {
    // Always use Finally to ensure that you exit the Monitor.
    // The following line creates another object containing 
    // the value of x, and throws SynchronizationLockException
    // because the two objects do not match.
    Monitor.Exit(x);
}

Bien qu'il soit possible de convertir une variable de type valeur avant d'appeler Enter et Exit, comme illustré dans l'exemple suivant, et de passer le même objet converti aux deux méthodes, cette opération n'offre aucun avantage. Les changements apportés à la variable n'apparaissent pas dans la copie convertie, et il est impossible de changer la valeur de cette copie.

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

Il est important de noter la distinction entre l'utilisation des objets Monitor et WaitHandle. Les objets Monitor sont purement managés, entièrement portables et peuvent s'avérer plus efficaces en termes d'exigences en matière de 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 de code suivant illustre l'utilisation associée de la classe Monitor (implémentée à l'aide du mot clé du compilateur) et la classe Interlocked.

Imports System
Imports System.Threading
Imports Microsoft.VisualBasic

' Note: The class whose internal public member is the synchronizing method
' is not public; none of the client code takes a lock on the Resource object.
' The member of the nonpublic class takes the lock on itself. Written this 
' way, malicious code cannot take a lock on a public object.
Class SyncResource
   
   Public Sub Access(threadNum As Int32)
      ' Uses Monitor class to enforce synchronization.
      SyncLock Me
         ' Synchronized: Despite the next conditional, each thread 
         ' waits on its predecessor.
         If threadNum Mod 2 = 0 Then
            Thread.Sleep(2000)
         End If
         Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum)
         Thread.Sleep(200)
         Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum)
      End SyncLock
   End Sub 'Access
End Class 'SyncResource

' Without the lock, the method is called in the order in which 
' threads reach it.
Class UnSyncResource
   
   Public Sub Access(threadNum As Int32)
      ' Does not use Monitor class to enforce synchronization.
      ' The next call throws the thread order.
      If threadNum Mod 2 = 0 Then
         Thread.Sleep(2000)
      End If
      Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum)
      Thread.Sleep(200)
      Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum)
   End Sub 'Access
End Class 'UnSyncResource

Public Class App
   Private Shared numAsyncOps As Int32 = 5
   Private Shared asyncOpsAreDone As New AutoResetEvent(False)
   Private Shared SyncRes As New SyncResource()
   Private Shared UnSyncRes As New UnSyncResource()
   Private Shared threadNum As Int32
   Public Shared Sub Main()
      
      For threadNum = 0 To 4
         ThreadPool.QueueUserWorkItem(AddressOf SyncUpdateResource, threadNum)
      Next threadNum
      
      ' Wait until this WaitHandle is signaled.
      asyncOpsAreDone.WaitOne()
      Console.WriteLine(ControlChars.Tab + ControlChars.Lf + "All synchronized operations have completed." + ControlChars.Lf)
      
      ' Reset the thread count for unsynchronized calls.
      numAsyncOps = 5
      
      For threadNum = 0 To 4
         ThreadPool.QueueUserWorkItem(AddressOf UnSyncUpdateResource, threadNum)
      Next threadNum
      
      ' Wait until this WaitHandle is signaled.
      asyncOpsAreDone.WaitOne()
      Console.WriteLine(ControlChars.Tab + ControlChars.Cr + "All unsynchronized thread operations have completed.")
   End Sub 'Main
   
   
   
   ' The callback method's signature MUST match that of 
   ' a System.Threading.TimerCallback delegate
   ' (it takes an Object parameter and returns void).
   Shared Sub SyncUpdateResource(state As Object)
      ' This calls the internal synchronized method, passing 
      ' a thread number.
      SyncRes.Access(CType(state, Int32))
      
      ' Count down the number of methods that the threads have called.
      ' This must be synchronized, however; you cannot know which thread 
      ' will access the value **before** another thread's incremented 
      ' value has been stored into the variable.
      If Interlocked.Decrement(numAsyncOps) = 0 Then
         asyncOpsAreDone.Set() 
         ' Announce to Main that in fact all thread calls are done.
      End If
   End Sub 'SyncUpdateResource
    
   ' The callback method's signature MUST match that of 
   ' a System.Threading.TimerCallback delegate
   ' (it takes an Object parameter and returns void).
   Shared Sub UnSyncUpdateResource(state As [Object])
      ' This calls the unsynchronized method, passing 
      ' a thread number.
      UnSyncRes.Access(CType(state, Int32))
      
      ' Count down the number of methods that the threads have called.
      ' This must be synchronized, however; you cannot know which thread 
      ' will access the value **before** another thread's incremented 
      ' value has been stored into the variable.
      If Interlocked.Decrement(numAsyncOps) = 0 Then
         asyncOpsAreDone.Set() 
         ' Announce to Main that in fact all thread calls are done.
      End If
   End Sub 'UnSyncUpdateResource 
End Class 'App
using System;
using System.Threading;

// Note: The class whose internal public member is the synchronizing 
// method is not public; none of the client code takes a lock on the 
// Resource object.The member of the nonpublic class takes the lock on 
// itself. Written this way, malicious code cannot take a lock on 
// a public object.
class SyncResource {
   public void Access(Int32 threadNum) {
      // Uses Monitor class to enforce synchronization.
      lock (this) {
       // Synchronized: Despite the next conditional, each thread 
       // waits on its predecessor.
       if (threadNum % 2 == 0)
         Thread.Sleep(2000);
         Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
         Thread.Sleep(200);
         Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
      }
   }
}

// Without the lock, the method is called in the order in which threads reach it.
class UnSyncResource {
   public void Access(Int32 threadNum) {
    // Does not use Monitor class to enforce synchronization.
    // The next call throws the thread order.
    if (threadNum % 2 == 0)
      Thread.Sleep(2000);
     Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
     Thread.Sleep(200);
     Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
   }
}

public class App {
   static Int32 numAsyncOps = 5;
   static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
   static SyncResource SyncRes = new SyncResource();
   static UnSyncResource UnSyncRes = new UnSyncResource();

   public static void Main() {

      for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
         ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
      }

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

     // Reset the thread count for unsynchronized calls.
     numAsyncOps = 5;

      for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
         ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
      }

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


   // The callback method's signature MUST match that of a 
   // System.Threading.TimerCallback delegate (it takes an Object 
   // parameter and returns void).
   static void SyncUpdateResource(Object state) {
     // This calls the internal synchronized method, passing 
     // a thread number.
      SyncRes.Access((Int32) state);

     // Count down the number of methods that the threads have called.
     // This must be synchronized, however; you cannot know which thread 
     // will access the value **before** another thread's incremented 
     // value has been stored into the variable.
      if (Interlocked.Decrement(ref numAsyncOps) == 0)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }

   // The callback method's signature MUST match that of a 
   // System.Threading.TimerCallback delegate (it takes an Object 
   // parameter and returns void).
   static void UnSyncUpdateResource(Object state) {
     // This calls the unsynchronized method, passing a thread number.
      UnSyncRes.Access((Int32) state);

     // Count down the number of methods that the threads have called.
     // This must be synchronized, however; you cannot know which thread 
     // will access the value **before** another thread's incremented 
     // value has been stored into the variable.
      if (Interlocked.Decrement(ref numAsyncOps) == 0)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }
}

Voir aussi

Référence

Monitor

Autres ressources

Fonctionnalités et objets de threading