Monitor Monitor Monitor Monitor Class

Definizione

Fornisce un meccanismo che sincronizza l'accesso agli oggetti.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
Ereditarietà
MonitorMonitorMonitorMonitor
Attributi

Esempi

Nell'esempio seguente viene usata Monitor la classe per sincronizzare l'accesso a una singola istanza di un generatore di numeri casuali rappresentato Random dalla 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. Nell'esempio vengono create dieci attività, ognuna delle quali viene eseguita in modo asincrono in un thread del pool di thread.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Ogni attività genera numeri casuali 10.000, calcola la media e aggiorna due variabili a livello di procedura che mantengono un totale parziale del numero di numeri casuali generati e della relativa somma.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. Dopo l'esecuzione di tutte le attività, questi due valori vengono quindi utilizzati per calcolare la media complessiva.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)

Poiché è possibile accedervi da qualsiasi attività in esecuzione su un thread del pool di thread, l' total accesso n alle variabili e deve essere sincronizzato.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. Per Interlocked.Add questo scopo, viene usato il metodo.The Interlocked.Add method is used for this purpose.

Nell'esempio seguente viene illustrato l'uso combinato della Monitor classe (implementata con lock il SyncLock costrutto del linguaggio o Interlocked ), la classe AutoResetEvent e 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. Definisce due classi internal (in C#) o Friend (in Visual Basic), SyncResource e UnSyncResource, che forniscono rispettivamente l'accesso sincronizzato e non sincronizzato a una risorsa.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. Per assicurarsi che l'esempio illustri la differenza tra l'accesso sincronizzato e non sincronizzato (che può essere il caso se ogni chiamata al metodo viene completata rapidamente), il metodo include un ritardo casuale: per i thread la cui proprietà Thread.ManagedThreadId è pari, il metodo chiama Thread.Sleep per introdurre un ritardo di 2000 millisecondi.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. Si noti che, perché la classe SyncResource non è pubblica, nessuna parte del codice client acquisisce un blocco sulla risorsa sincronizzata: è la classe interna ad acquisire il blocco.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. Ciò impedisce l'acquisizione di un blocco su un oggetto pubblico da parte di codice dannoso.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'esempio definisce una variabile, numOps, che definisce il numero di thread che proverà ad accedere alla risorsa.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. Il thread dell'applicazione chiama per cinque volte ciascun metodo ThreadPool.QueueUserWorkItem(WaitCallback) per l'accesso sincronizzato e non sincronizzato.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. Il metodo ThreadPool.QueueUserWorkItem(WaitCallback) presenta un solo parametro, un delegato che non accetta parametri e non restituisce alcun valore.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. Per l'accesso sincronizzato, richiama il metodo SyncUpdateResource; per l'accesso non sincronizzato, richiama il metodo UnSyncUpdateResource.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Dopo ogni set di chiamate al metodo, il thread dell'applicazione chiama il metodo AutoResetEvent. WaitOne in modo che AutoResetEvent si blocchi fino a quando l'istanza non viene segnalata.After each set of method calls, the application thread calls the AutoResetEvent.WaitOne method so that it blocks until the AutoResetEvent instance is signaled.

Ogni chiamata al metodo SyncUpdateResource chiama il metodo interno SyncResource.Access e quindi chiama il metodo Interlocked.Decrement per decrementare il contatore 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. Il Interlocked.Decrement metodo viene usato per decrementare il contatore, perché in caso contrario non è possibile essere certi che un secondo thread possa accedere al valore prima che il valore decrementato di un primo thread sia archiviato nella variabile.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. Quando l'ultimo thread di lavoro sincronizzato decrementa il contatore a zero, a indicare che tutti i thread sincronizzati hanno completato l' SyncUpdateResource accesso alla risorsa EventWaitHandle.Set , il metodo chiama il metodo, che segnala al thread principale di continuare esecuzione.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.

Ogni chiamata al metodo UnSyncUpdateResource chiama il metodo interno UnSyncResource.Access e quindi chiama il metodo Interlocked.Decrement per decrementare il contatore 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. Ancora una volta, Interlocked.Decrement il metodo viene usato per decrementare il contatore per assicurarsi che un secondo thread non acceda al valore prima che il valore decrementato di un primo thread sia stato assegnato alla variabile.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. Quando l'ultimo thread di lavoro non sincronizzato decrementa il contatore a zero, a indicare che non sono più presenti thread non sincronizzati che devono accedere UnSyncUpdateResource alla risorsa, EventWaitHandle.Set il metodo chiama il metodo, che segnala al thread principale di continuare l'esecuzione .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.

Come mostra il risultato dell'esempio, l'accesso sincronizzato garantisce che il thread di chiamata esca dalla risorsa protetta prima che un altro thread possa accedervi; ogni thread attende il suo predecessore.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. D'altra parte, senza il blocco, il metodo UnSyncResource.Access viene chiamato nell'ordine in cui i thread lo raggiungono.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Commenti

La Monitor classe consente di sincronizzare l'accesso a un'area di codice prendendo e rilasciando un blocco su un oggetto particolare chiamando i Monitor.Entermetodi, Monitor.TryEntere Monitor.Exit .The Monitor class allows you to synchronize access to a region of code by taking and releasing a lock on a particular object by calling the Monitor.Enter, Monitor.TryEnter, and Monitor.Exit methods. I blocchi di oggetti offrono la possibilità di limitare l'accesso a un blocco di codice, comunemente denominato sezione critica.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. Mentre un thread è proprietario del blocco per un oggetto, nessun altro thread può acquisire tale blocco.While a thread owns the lock for an object, no other thread can acquire that lock. È anche possibile usare la Monitor classe per assicurarsi che nessun altro thread sia autorizzato ad accedere a una sezione del codice dell'applicazione eseguita dal proprietario del blocco, a meno che l'altro thread non esegua il codice usando un oggetto bloccato diverso.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.

Contenuto dell'articolo:In this article:

Classe Monitor: Panoramica The Monitor class: An overview
Oggetto Lock The lock object
Sezione critica The critical section
Pulse, PulseAll e Wait Pulse, PulseAll, and Wait
Monitoraggi e handle di attesaMonitors and wait handles

Classe Monitor: PanoramicaThe Monitor class: An overview

Monitorin sono disponibili le funzionalità seguenti:Monitor has the following features:

  • È associato a un oggetto su richiesta.It is associated with an object on demand.

  • Non è associato, il che significa che può essere chiamato direttamente da qualsiasi contesto.It is unbound, which means it can be called directly from any context.

  • Non è possibile creare Monitor un'istanza della classe. i metodi Monitor della classe sono tutti statici.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. A ogni metodo viene passato l'oggetto sincronizzato che controlla l'accesso alla sezione critica.Each method is passed the synchronized object that controls access to the critical section.

Nota

Utilizzare la Monitor classe per bloccare oggetti diversi da stringhe, ovvero tipi di riferimento diversi da String, e non tipi di valore.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Per informazioni dettagliate, vedere gli overload del Enter metodo e la sezione Lock Object più avanti in questo articolo.For details, see the overloads of the Enter method and The lock object section later in this article.

Nella tabella seguente vengono descritte le azioni che possono essere eseguite dai thread che accedono a oggetti sincronizzati:The following table describes the actions that can be taken by threads that access synchronized objects:

OperazioneAction DescriptionDescription
Enter, TryEnterEnter, TryEnter Acquisisce un blocco per un oggetto.Acquires a lock for an object. Questa azione contrassegna anche l'inizio di una sezione critica.This action also marks the beginning of a critical section. Nessun altro thread può entrare nella sezione critica a meno che non esegua le istruzioni nella sezione critica usando un oggetto bloccato diverso.No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait Rilascia il blocco su un oggetto per consentire ad altri thread di bloccare e accedere all'oggetto.Releases the lock on an object in order to permit other threads to lock and access the object. Il thread chiamante resta in attesa mentre un altro thread accede all'oggetto.The calling thread waits while another thread accesses the object. I segnali di impulso vengono usati per notificare ai thread in attesa le modifiche allo stato di un oggetto.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (signal), PulseAllPulse (signal), PulseAll Invia un segnale a uno o più thread in attesa.Sends a signal to one or more waiting threads. Il segnale notifica a un thread in attesa che lo stato dell'oggetto bloccato è stato modificato e il proprietario del blocco è pronto per rilasciare il blocco.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. Il thread in attesa viene inserito nella coda di disponibilità dell'oggetto, in modo che possa ricevere il blocco per l'oggetto.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Quando il thread ha il blocco, può controllare il nuovo stato dell'oggetto per verificare se è stato raggiunto lo stato richiesto.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 Rilascia il blocco su un oggetto.Releases the lock on an object. Questa azione contrassegna anche la fine di una sezione critica protetta dall'oggetto bloccato.This action also marks the end of a critical section protected by the locked object.

A partire da .NET Framework 4.NET Framework 4, sono disponibili due set di overload per i Enter metodi e TryEnter .Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Un set di overload dispone di un ref parametro (in C#) o ByRef (in Visual Basic) Boolean che viene impostato in modo atomico su true se il blocco viene acquisito, anche se viene generata un'eccezione durante l'acquisizione del blocco.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. Usare questi overload se è fondamentale rilasciare il blocco in tutti i casi, anche quando le risorse protette dal blocco potrebbero non essere coerenti.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.

Oggetto LockThe lock object

La classe Monitor è costituita dai static metodi ( Shared in C#) o (in Visual Basic) che operano su un oggetto che controlla l'accesso alla sezione critica.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. Per ogni oggetto sincronizzato vengono mantenute le informazioni seguenti:The following information is maintained for each synchronized object:

  • Riferimento al thread che attualmente possiede il blocco.A reference to the thread that currently holds the lock.

  • Riferimento a una coda pronta che contiene i thread pronti per ottenere il blocco.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Riferimento a una coda in attesa, che contiene i thread in attesa della notifica di una modifica nello stato dell'oggetto bloccato.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 blocca gli oggetti, ovvero i tipi di riferimento, non i tipi di valore.Monitor locks objects (that is, reference types), not value types. Anche se è possibile passare un tipo di valore a Enter e Exit, ogni valore viene sottoposto a boxing separatamente per ogni chiamata.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Poiché ogni chiamata crea un oggetto separato, Enter non si blocca mai e non sincronizza realmente il codice che dovrebbe proteggere.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. L'oggetto passato a Exit, inoltre, è diverso dall'oggetto passato a Enter, quindi Monitor genera un'eccezione SynchronizationLockException con il messaggio "Il metodo di sincronizzazione dell'oggetto è stato chiamato da un blocco di codice non sincronizzato".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'esempio seguente illustra questo problema.The following example illustrates this problem. Avvia dieci attività, ognuna delle quali rimane semplicemente inattiva per 250 millisecondi.It launches ten tasks, each of which just sleeps for 250 milliseconds. Ogni attività aggiorna quindi una variabile del contatore, nTasks, in modo da contare il numero di attività effettivamente avviate ed eseguite.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Poiché nTasks è una variabile globale che può essere aggiornata da più attività contemporaneamente, viene usato un oggetto monitor per proteggerla dalla modifica simultanea da parte di più attività.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. Tuttavia, come illustrato dall'output dell'esempio, ogni attività genera un'eccezione SynchronizationLockException.However, as the output from the example shows, each of the tasks throws a SynchronizationLockException exception.

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

public class Example
{
   public static void Main()
   {

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

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

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

Ogni attività genera un'eccezione SynchronizationLockException, poiché la variabile nTasks viene sottoposta a boxing prima della chiamata al metodo Monitor.Enter in ogni attività.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. In altri termini, una variabile separata, indipendente dalle altre, viene passata a ogni chiamata al metodo.In other words, each method call is passed a separate variable that is independent of the others. nTasks viene sottoposto di nuovo a boxing nella chiamata al metodo Monitor.Exit.nTasks is boxed again in the call to the Monitor.Exit method. Ancora una volta, ciò crea dieci nuove variabili di tipo boxed, indipendenti le une dalle altre, nTasks e le dieci variabili di tipo boxed create nella chiamata al metodo 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'eccezione viene generata, quindi, poiché il codice sta tentando di rilasciare un blocco su una variabile appena creata non bloccata in precedenza.The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

Anche se è possibile sottoporre a boxing una variabile di tipo valore prima di chiamare Enter e Exit, come illustrato nell'esempio seguente, e passare lo stesso oggetto di tipo boxed a entrambi i metodi, ciò non presenta alcun vantaggio.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. Le modifiche alla variabile di tipo unboxed non sono riflesse nella copia di tipo boxed e non è possibile modificare il valore della copia di tipo boxed.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.

Quando si seleziona un oggetto in cui eseguire la sincronizzazione, è necessario bloccare solo gli oggetti interni o privati.When selecting an object on which to synchronize, you should lock only on private or internal objects. Il blocco su oggetti esterni può comportare deadlock, perché il codice non correlato può scegliere gli stessi oggetti su cui eseguire il blocco per scopi diversi.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Si noti che è possibile eseguire la sincronizzazione su un oggetto in più domini dell'applicazione se l'oggetto usato per il MarshalByRefObjectblocco deriva da.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

Sezione criticaThe critical section

Usare i Enter metodi Exit e per contrassegnare l'inizio e la fine di una sezione critica.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Nota

La funzionalità fornita Enter dai metodi e Exit è identica a quella fornita dall'istruzione Lock in C# e dall'istruzione SyncLock in Visual Basic, ad eccezione del fatto che i costrutti di linguaggio incapsulano Monitor.Enter(Object, Boolean)overload del Monitor.Exit metodo e metodo in un try...finallyThe functionality provided by the Enter and Exit methods is identical to that provided by the lock statement in C# and the SyncLock statement in Visual Basic, except that the language constructs wrap the Monitor.Enter(Object, Boolean) method overload and the Monitor.Exit method in a tryfinally per assicurarsi che il monitoraggio venga rilasciato.block to ensure that the monitor is released.

Se la sezione critica è un set di istruzioni contigue, il blocco acquisito dal Enter metodo garantisce che solo un singolo thread possa eseguire il codice racchiuso con l'oggetto bloccato.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. In questo caso, è consigliabile inserire il codice in un try blocco e inserire la chiamata Exit al metodo in un finally blocco.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. Ciò assicura che il blocco venga rilasciato anche se si verifica un'eccezione.This ensures that the lock is released even if an exception occurs. Il frammento di codice seguente illustra questo modello.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

Questa funzionalità viene in genere utilizzata per sincronizzare l'accesso a un metodo statico o di istanza di una classe.This facility is typically used to synchronize access to a static or instance method of a class.

Se una sezione critica si estende su un intero metodo, è possibile ottenere la funzione di blocco inserendo System.Runtime.CompilerServices.MethodImplAttribute l'oggetto nel metodo e specificando Synchronized il valore nel costruttore di System.Runtime.CompilerServices.MethodImplAttribute.If a critical section spans an entire method, the locking facility can be achieved by placing the System.Runtime.CompilerServices.MethodImplAttribute on the method, and specifying the Synchronized value in the constructor of System.Runtime.CompilerServices.MethodImplAttribute. Quando si usa questo attributo, le Enter chiamate Exit al metodo e non sono necessarie.When you use this attribute, the Enter and Exit method calls are not needed. Il frammento di codice seguente illustra questo modello:The following code fragment illustrates this pattern:

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

Si noti che l'attributo determina il mantenimento del blocco da parte del thread corrente fino a quando il metodo non restituisce; Se il blocco può essere rilasciato prima, utilizzare la Monitor classe, l' C# istruzione Lock o l'istruzione Visual Basic SyncLock all'interno del metodo anziché l'attributo.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.

Sebbene sia possibile per le istruzioni Enter e Exit che bloccano e rilasciano un determinato oggetto per superare i limiti di membri o classi o entrambi, questa procedura non è consigliata.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 e WaitPulse, PulseAll, and Wait

Una volta che un thread è proprietario del blocco e è entrato nella sezione critica protetta dal blocco, può chiamare Monitor.Waiti Monitor.Pulsemetodi, Monitor.PulseAll e.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.

Quando il thread che include il blocco chiama Wait, il blocco viene rilasciato e il thread viene aggiunto alla coda di attesa dell'oggetto sincronizzato.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. Il primo thread della coda di disponibilità, se disponibile, acquisisce il blocco e immette la sezione critica.The first thread in the ready queue, if any, acquires the lock and enters the critical section. Il thread che ha Wait chiamato viene spostato dalla coda di attesa alla coda di disponibilità quando il Monitor.Pulse Monitor.PulseAll metodo o viene chiamato dal thread che include il blocco (da spostare, il thread deve trovarsi all'inizio della coda di attesa).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). Il Wait metodo restituisce quando il thread chiamante riacquisisce il blocco.The Wait method returns when the calling thread reacquires the lock.

Quando il thread che include il blocco chiama Pulse, il thread all'inizio della coda di attesa viene spostato nella coda di thread pronti.When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. La chiamata al PulseAll metodo sposta tutti i thread dalla coda in attesa alla coda di pronto.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitoraggi e handle di attesaMonitors and wait handles

È importante notare la distinzione tra l'uso della Monitor classe e WaitHandle degli oggetti.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • La Monitor classe è puramente gestita, completamente portabile e potrebbe essere più efficiente in termini di requisiti delle risorse del sistema operativo.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • Gli oggetti WaitHandle rappresentano oggetti awaitable del sistema operativo, sono utili per la sincronizzazione tra codice gestito e non gestito ed espongono alcune funzionalità avanzate del sistema operativo, ad esempio la capacità di rimanere in attesa di più oggetti contemporaneamente.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.

Proprietà

LockContentionCount LockContentionCount LockContentionCount LockContentionCount

Ottiene il numero di volte in cui si è verificata una contesa durante il tentativo di acquisire il blocco del monitoraggio.Gets the number of times there was contention when trying to take the monitor's lock.

Metodi

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

Acquisisce un blocco esclusivo sull'oggetto specificato.Acquires an exclusive lock on the specified object.

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

Acquisisce un blocco esclusivo sull'oggetto specificato e imposta atomicamente un valore che indica se il blocco è stato ottenuto.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)

Viene rilasciato un blocco esclusivo sull'oggetto specificato.Releases an exclusive lock on the specified object.

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

Determina se il thread corrente specificato contiene il blocco sull'oggetto specificato.Determines whether the current thread holds the lock on the specified object.

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

Consente di notificare a un thread della coda di attesa che lo stato dell'oggetto bloccato è stato modificato.Notifies a thread in the waiting queue of a change in the locked object's state.

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

Consente di notificare a tutti i thread in attesa che lo stato dell'oggetto è stato modificato.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)

Prova ad acquisire, per la quantità di tempo specificata, un blocco esclusivo sull'oggetto specificato e imposta atomicamente un valore che indica se il blocco è stato ottenuto.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)

Prova ad acquisire, per il numero di millisecondi specificato, un blocco esclusivo sull'oggetto specificato e imposta atomicamente un valore che indica se il blocco è stato ottenuto.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)

Viene eseguito, per una quantità di tempo specificata, il tentativo di acquisire un blocco esclusivo sull'oggetto specificato.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)

Prova ad acquisire un blocco esclusivo sull'oggetto specificato e imposta atomicamente un valore che indica se il blocco è stato ottenuto.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)

Prova ad acquisire un blocco esclusivo sull'oggetto specificato.Attempts to acquire an exclusive lock on the specified object.

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

Viene eseguito, per un numero specificato di millisecondi, il tentativo di acquisire un blocco esclusivo sull'oggetto specificato.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)

Rilascia il blocco su un oggetto e interrompe il thread corrente finché riacquisisce il blocco.Releases the lock on an object and blocks the current thread until it reacquires the lock. Allo scadere dell'intervallo di timeout specificato, il thread viene inserito nella coda di thread pronti.If the specified time-out interval elapses, the thread enters the ready queue. Questo metodo consente anche di specificare se il dominio di sincronizzazione per il contesto, qualora si trovi in un contesto di sincronizzazione, viene terminato prima dell'attesa e riacquisito in un secondo momento.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)

Rilascia il blocco su un oggetto e interrompe il thread corrente finché riacquisisce il blocco.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)

Rilascia il blocco su un oggetto e interrompe il thread corrente finché riacquisisce il blocco.Releases the lock on an object and blocks the current thread until it reacquires the lock. Allo scadere dell'intervallo di timeout specificato, il thread viene inserito nella coda di thread pronti.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)

Rilascia il blocco su un oggetto e interrompe il thread corrente finché riacquisisce il blocco.Releases the lock on an object and blocks the current thread until it reacquires the lock. Allo scadere dell'intervallo di timeout specificato, il thread viene inserito nella coda di thread pronti.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)

Rilascia il blocco su un oggetto e interrompe il thread corrente finché riacquisisce il blocco.Releases the lock on an object and blocks the current thread until it reacquires the lock. Allo scadere dell'intervallo di timeout specificato, il thread viene inserito nella coda di thread pronti.If the specified time-out interval elapses, the thread enters the ready queue. Esce eventualmente dal dominio di sincronizzazione per il contesto di sincronizzazione prima dell'attesa e riacquisisce il dominio in un secondo momento.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

Si applica a

Thread safety

Questo tipo è thread-safe.This type is thread safe.

Vedi anche