Monitor Monitor Monitor Monitor Class

Definición

Proporciona un mecanismo que sincroniza el acceso a los objetos.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
Herencia
MonitorMonitorMonitorMonitor
Atributos

Ejemplos

En el ejemplo siguiente se usa el Monitor clase para sincronizar el acceso a una sola instancia de un generador de números aleatorios, representado por la Random clase.The following example uses the Monitor class to synchronize access to a single instance of a random number generator represented by the Random class. El ejemplo crea diez tareas, cada uno de los cuales se ejecuta de forma asincrónica en un subproceso ThreadPool.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Cada tarea genera números aleatorios 10.000, calcula su Media y actualiza dos variables de nivel de procedimiento que mantienen un total acumulado del número de números aleatorios generados y su suma.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. Después de que se han ejecutado todas las tareas, estos dos valores, a continuación, se utilizan para calcular la media general.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)

Dado que se pueden acceder desde cualquier tarea que se ejecuta en un subproceso ThreadPool, acceder a las variables total y n también deben estar sincronizadas.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. El Interlocked.Add método se usa para este propósito.The Interlocked.Add method is used for this purpose.

En el ejemplo siguiente se muestra el uso combinado de la Monitor clase (implementada con el lock o SyncLock construcción de lenguaje), el Interlocked (clase) y el AutoResetEvent clase.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. Define dos clases de tipo internal (en C#) o Friend (en Visual Basic), SyncResource y UnSyncResource, que proporcionan respectivamente acceso sincronizado y sin sincronizar a un recurso.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. Para garantizar que el ejemplo ilustra la diferencia entre el acceso sincronizado y sin sincronizar (podría darse el caso si cada llamada al método se realiza rápidamente), el método incluye un retraso aleatorio: para subprocesos cuya propiedad Thread.ManagedThreadId es par, el método llama a Thread.Sleep para insertar un retraso de 2.000 milisegundos.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. Tenga en cuenta que, dado que la clase SyncResource no es pública, ninguno de los códigos de cliente tiene un bloqueo en el recurso sincronizado; esto es, la propia clase interna se hace cargo del bloqueo.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. Esto evita que cualquier código malintencionado se haga cargo de un bloqueo en un objeto público.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.

En el ejemplo se define la variable numOps, que define el número de subprocesos que intentarán tener acceso al recurso.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. El subproceso de la aplicación llama al método ThreadPool.QueueUserWorkItem(WaitCallback) para obtener acceso sincronizado y sin sincronizar; esto se realizará hasta un máximo de cinco veces con cada opción.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. El método ThreadPool.QueueUserWorkItem(WaitCallback) tiene un único parámetro; un delegado que no acepta parámetros y que no devuelve valor alguno.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. Para obtener acceso sincronizado, este elemento invoca al método SyncUpdateResource; en cambio, para obtener acceso no sincronizado, invoca al método UnSyncUpdateResource.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Después de cada conjunto de llamadas de método, el subproceso de la aplicación llama a la AutoResetEvent.WaitOne método para que así se bloquee hasta que la AutoResetEvent se señala la instancia.After each set of method calls, the application thread calls the AutoResetEvent.WaitOne method so that it blocks until the AutoResetEvent instance is signaled.

Cada llamada al métodoSyncUpdateResource, llama al método interno SyncResource.Access y, a continuación, llama al método Interlocked.Decrement para reducir el contador 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. El Interlocked.Decrement método se usa para reducir el contador, porque de lo contrario, no puede estar seguro de que un segundo subproceso acceso al valor reducido del primer subproceso antes de valor se ha almacenado en la variable.The Interlocked.Decrement method Is used to decrement the counter, because otherwise you cannot be certain that a second thread will access the value before a first thread's decremented value has been stored in the variable. Cuando el último sincroniza disminuye de subproceso de trabajo en el contador a cero, que indica que todos los subprocesos sincronizados han accedido al recurso, el SyncUpdateResource llamadas al método el EventWaitHandle.Set método, que indica al subproceso principal para continuar ejecución.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.

Cada llamada al métodoUnSyncUpdateResource, llama al método interno UnSyncResource.Access y, a continuación, llama al método Interlocked.Decrement para reducir el contador 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. Una vez más, la Interlocked.Decrement método se usa para reducir el contador para asegurarse de que un segundo subproceso no tiene acceso el valor antes de que se ha asignado el valor reducido del primer subproceso a la variable.Once again, the Interlocked.Decrement method Is used to decrement the counter to ensure that a second thread does not access the value before a first thread's decremented value has been assigned to the variable. Cuando el último no sincronizadas disminuye de subproceso de trabajo que el contador a cero, que indica que no hay más subprocesos no sincronizados necesita tener acceso al recurso, el UnSyncUpdateResource llamadas al método el EventWaitHandle.Set método, que indica al subproceso principal para continuar la ejecución .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.

Como se muestra en el resultado del ejemplo, el acceso sincronizado garantiza que el subproceso de llamada saldrá del recurso protegido antes de que otro subproceso pueda tener acceso a él; así pues, cada subproceso esperará a su predecesor.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. Por otro lado, sin este bloqueo, se llamará al método UnSyncResource.Access en el orden en el que los subprocesos accedan a él.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Comentarios

El Monitor clase le permite sincronizar el acceso a una región de código mediante la obtención y liberación de un bloqueo en un objeto determinado mediante una llamada a la Monitor.Enter, Monitor.TryEnter, y Monitor.Exit métodos.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. Los bloqueos de objeto proporcionan la capacidad para restringir el acceso a un bloque de código, normalmente denominado una sección crítica.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. Mientras un subproceso posee el bloqueo de un objeto, ningún otro subproceso puede adquirir ese bloqueo.While a thread owns the lock for an object, no other thread can acquire that lock. También puede usar el Monitor clase para asegurarse de que ningún otro subproceso puede tener acceso a una sección de la aplicación de código que se está ejecutando por el propietario del bloqueo, a menos que el otro subproceso está ejecutando el código que usa un objeto bloqueado distinto.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.

En este artículo:In this article:

La clase Monitor: Información general The Monitor class: An overview
El objeto de bloqueo The lock object
La sección crítica The critical section
Pulse, PulseAll y espera Pulse, PulseAll, and Wait
Monitores y los identificadores de esperaMonitors and wait handles

La clase Monitor: Información generalThe Monitor class: An overview

Monitor tiene las siguientes características:Monitor has the following features:

  • Está asociado con un objeto a petición.It is associated with an object on demand.

  • Es independiente, lo que significa que puede llamarse directamente desde cualquier contexto.It is unbound, which means it can be called directly from any context.

  • Una instancia de la Monitor no se puede crear la clase; los métodos de la Monitor clase son todos estáticos.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. Cada método se pasa el objeto sincronizado que controla el acceso a la sección crítica.Each method is passed the synchronized object that controls access to the critical section.

Nota

Use la Monitor clase para objetos de bloqueo que no sean cadenas (es decir, tipos de referencia distinto String), no los tipos de valor.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Para obtener más información, vea las sobrecargas de los Enter método y el objeto de bloqueo sección más adelante en este artículo.For details, see the overloads of the Enter method and The lock object section later in this article.

En la tabla siguiente se describe las acciones que pueden realizarse por los subprocesos que tienen acceso a los objetos sincronizados:The following table describes the actions that can be taken by threads that access synchronized objects:

AcciónAction DescripciónDescription
Enter, TryEnterEnter, TryEnter Adquiere un bloqueo de un objeto.Acquires a lock for an object. Esta acción también marca el principio de una sección crítica.This action also marks the beginning of a critical section. Ningún otro subproceso puede entrar en la sección crítica, a menos que se ejecuta las instrucciones que aparecen en la sección crítica utilizando un objeto bloqueado distinto.No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait Libera el bloqueo en un objeto con el fin de permitir que otros subprocesos de bloqueo y obtener acceso al objeto.Releases the lock on an object in order to permit other threads to lock and access the object. Espera a que el subproceso de llamada mientras otro subproceso tiene acceso al objeto.The calling thread waits while another thread accesses the object. Las señales de pulsos se usan para notificar a los subprocesos en espera sobre los cambios en el estado de un objeto.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (señal), PulseAllPulse (signal), PulseAll Envía una señal a uno o varios subprocesos en espera.Sends a signal to one or more waiting threads. La señal notifica a un subproceso en espera que se ha cambiado el estado del objeto bloqueado, y el propietario del bloqueo está listo para liberar el bloqueo.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. El subproceso en espera se coloca en la cola del objeto listo para que pueda recibir el bloqueo para el objeto.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Una vez que el subproceso tiene el bloqueo, puede comprobar el estado nueva del objeto para ver si se ha alcanzado el estado necesario.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 Libera el bloqueo en un objeto.Releases the lock on an object. Esta acción también marca el final de una sección crítica protegida por el objeto bloqueado.This action also marks the end of a critical section protected by the locked object.

A partir del .NET Framework 4.NET Framework 4, hay dos conjuntos de sobrecargas para el Enter y TryEnter métodos.Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Un conjunto de sobrecargas tiene un ref (en C#) o ByRef (en Visual Basic) Boolean parámetro que se establece de forma atómica en true si se adquiere el bloqueo, incluso si se produce una excepción al adquirir el bloqueo.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. Use estas sobrecargas si es fundamental para liberar el bloqueo en todos los casos, incluso cuando los recursos que se está protegiendo el bloqueo no esté en un estado coherente.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.

El objeto de bloqueoThe lock object

La clase Monitor consta de static (en C#) o Shared (en Visual Basic) entre los métodos que operan en un objeto que controla el acceso a la sección crítica.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. La siguiente información se mantiene para cada objeto sincronizado:The following information is maintained for each synchronized object:

  • Una referencia al subproceso que actualmente mantiene el bloqueo.A reference to the thread that currently holds the lock.

  • Una referencia a una cola de subprocesos listos, que contiene los subprocesos que están listos para obtener el bloqueo.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Una referencia a una cola de espera, que contiene los subprocesos que esperan para recibir notificaciones de un cambio en el estado del objeto bloqueado.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 bloquea objetos (es decir, tipos de referencia), no tipos de valor.Monitor locks objects (that is, reference types), not value types. Aunque puede pasar un tipo de valor a Enter y Exit, se somete a una conversión boxing independiente para cada llamada.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Puesto que cada llamada crea un objeto independiente, Enter nunca bloquea y el código al que supuestamente protege no está sincronizado realmente.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. Además, el objeto que se pasa a Exit es diferente del objeto que se pasa a Enter, por lo que Monitor produce la excepción SynchronizationLockException con el mensaje “El método de sincronización del objeto se ha llamado desde un bloque de códigos sin sincronizar.”.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."

El siguiente ejemplo ilustra este problema.The following example illustrates this problem. Inicia diez tareas, cada una de los cuales solo se suspende durante 250 milisegundos.It launches ten tasks, each of which just sleeps for 250 milliseconds. A continuación, cada tarea actualiza una variable de contador, nTasks, pensada para contar el número de tareas que realmente se iniciaron y ejecutaron.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Dado que nTasks es una variable global que pueden actualizar varias tareas al mismo tiempo, se usa un monitor para protegerla frente a modificaciones simultáneas de varias tareas.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. Sin embargo, como muestra la salida del ejemplo, cada una de las tareas produce una excepción 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.

Cada tarea produce una excepción SynchronizationLockException porque la variable nTasks es objeto de a una conversión boxing antes de llamar al método Monitor.Enter en cada tarea.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. En otras palabras, a cada llamada al método se le pasa una variable independiente, que es independiente del resto.In other words, each method call is passed a separate variable that is independent of the others. nTasks se vuelve a someter a una conversión boxing en la llamada al método Monitor.Exit.nTasks is boxed again in the call to the Monitor.Exit method. Una vez más, esto crea diez nuevas variables sometidas a conversión boxing, que son independientes entre sí, nTasks, y las diez variables creadas en la llamada al método Monitor.Enter y sometidas a una conversión boxing.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. A continuación, se produce la excepción porque el código está intentando liberar un bloqueo en una variable recién creada que no se ha bloqueado anteriormente.The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

Aunque puede aplicar una conversión boxing a una variable de tipo de valor antes de llamar a Enter y Exit, tal como se muestra en el siguiente ejemplo, y pasar el mismo objeto sometido a conversión boxing a ambos métodos, hacerlo no ofrece ninguna ventaja.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. Los cambios realizados en la variable sometida a conversión unboxing no se reflejan en la copia sometida a conversión boxing, y no hay ninguna forma de cambiar el valor de la copia de sometida a conversión boxing.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.

Al seleccionar un objeto en el que se va a sincronizar, debe bloquear solo en objetos privados o internos.When selecting an object on which to synchronize, you should lock only on private or internal objects. El bloqueo de objetos externos, se podría producir interbloqueos, porque el código relacionado con podría elegir los mismos objetos para bloquear para distintos fines.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Tenga en cuenta que puede sincronizar en un objeto en varios dominios de aplicación si el objeto usado para el bloqueo deriva MarshalByRefObject.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

La sección críticaThe critical section

Use la Enter y Exit métodos para marcar el principio y al final de una sección crítica.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Nota

La funcionalidad proporcionada por el Enter y Exit métodos es idéntica a la que ofrece el bloqueo instrucción en C# y el SyncLock instrucción en Visual Basic, salvo que el construcciones de lenguaje encapsulado el Monitor.Enter(Object, Boolean) sobrecarga del método y el Monitor.Exit método en 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 bloque para asegurarse de que se libera el monitor.block to ensure that the monitor is released.

Si la sección crítica es un conjunto de instrucciones contiguas, a continuación, el bloqueo adquirido por el Enter método garantiza que solo un subproceso puede ejecutar el código delimitado con el objeto bloqueado.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. En este caso, se recomienda colocar ese código en un try bloquear y realizar una llamada a la Exit método en un finally bloque.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. Esto garantiza la liberación del bloqueo aunque se produzca una excepción.This ensures that the lock is released even if an exception occurs. El siguiente fragmento de código muestra este patrón.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

Esta función normalmente se utiliza para sincronizar el acceso a ningún elemento estático o un método de instancia de una clase.This facility is typically used to synchronize access to a static or instance method of a class.

Si una sección crítica abarca todo un método, la utilidad de bloqueo puede lograrse mediante la colocación de la System.Runtime.CompilerServices.MethodImplAttribute en el método y especificando el Synchronized valor en el constructor de 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. Cuando se utiliza este atributo, el Enter y Exit no son necesarias las llamadas a métodos.When you use this attribute, the Enter and Exit method calls are not needed. El siguiente fragmento de código muestra este patrón:The following code fragment illustrates this pattern:

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

Tenga en cuenta que el atributo hace que el subproceso actual mantiene el bloqueo hasta que el método devuelve; Si el bloqueo puede liberarse antes, utilice la Monitor clase C#, bloqueo instrucción o Visual Basic SyncLock instrucción dentro del método en lugar del atributo.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.

Aunque es posible que el Enter y Exit las instrucciones que bloquear y liberar un objeto determinado para cruzar los límites de clase y miembro, no se recomienda esta práctica.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 y esperaPulse, PulseAll, and Wait

Una vez que un subproceso posee el bloqueo y ha entrado en la sección crítica que protege el bloqueo, puede llamar a la Monitor.Wait, Monitor.Pulse, y Monitor.PulseAll métodos.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.

Cuando el subproceso que contiene las llamadas de bloqueo Wait, se libera el bloqueo y el subproceso se agrega a la cola de espera del objeto sincronizado.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. El primer subproceso en la cola de subprocesos listos, si los hay, adquiere el bloqueo y entra en la sección crítica.The first thread in the ready queue, if any, acquires the lock and enters the critical section. El subproceso se mueve desde la cola de espera para la cola lista cuando ya sea el Monitor.Pulse (para mover, el subproceso debe estar en el encabezado de la cola de espera) o el Monitor.PulseAll se llama al método por el subproceso que mantiene el bloqueo.The thread is moved from the waiting queue to the ready queue when either the Monitor.Pulse (to be moved, the thread must be at the head of the waiting queue) or the Monitor.PulseAll method is called by the thread that holds the lock. El Wait método devuelve cuando el subproceso que realiza la llamada vuelve a adquirir el bloqueo.The Wait method returns when the calling thread reacquires the lock.

Cuando el subproceso que contiene las llamadas de bloqueo Pulse, el subproceso en el encabezado de la cola de espera se mueve a la cola de subprocesos listos.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 llamada a la PulseAll método mueve todos los subprocesos de la cola de espera a la cola de subprocesos listos.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitores y los identificadores de esperaMonitors and wait handles

Es importante tener en cuenta la distinción entre el uso de la Monitor clase y WaitHandle objetos.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • La Monitor clase es puramente administrados, son totalmente portátiles y podrían ser más eficaces en cuanto a los requisitos de recursos del sistema operativo.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • Los objetos WaitHandle representan objetos del sistema operativo que pueden esperar, que son útiles para la sincronización entre el código administrado y el no administrado y exponen algunas características avanzadas del sistema operativo, como la capacidad de esperar muchos objetos a la vez.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.

Métodos

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

Adquiere un bloqueo exclusivo en el objeto especificado.Acquires an exclusive lock on the specified object.

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

Adquiere un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.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)

Libera un bloqueo exclusivo en el objeto especificado.Releases an exclusive lock on the specified object.

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

Determina si el subproceso actual mantiene el bloqueo en el objeto especificado.Determines whether the current thread holds the lock on the specified object.

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

Notifica un cambio de estado del objeto bloqueado al subproceso que se encuentra en la cola de espera.Notifies a thread in the waiting queue of a change in the locked object's state.

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

Notifica un cambio de estado del objeto a todos los subprocesos que se encuentran en espera.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)

Intenta, durante el periodo de tiempo indicado, adquirir un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.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)

Intenta, durante el número especificado de milisegundos, adquirir un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.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)

Intenta adquirir un bloqueo exclusivo en el objeto especificado durante el período de tiempo especificado.Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object.

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

Intenta adquirir un bloqueo exclusivo en el objeto especificado durante el número de segundos especificado.Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object.

TryEnter(Object, Boolean) TryEnter(Object, Boolean) TryEnter(Object, Boolean) TryEnter(Object, Boolean)

Intenta adquirir un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.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)

Intenta adquirir un bloqueo exclusivo en el objeto especificado.Attempts to acquire an exclusive lock on the specified object.

Wait(Object) Wait(Object) Wait(Object) Wait(Object)

Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelve a adquirir el bloqueo.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)

Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelve a adquirir el bloqueo.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si transcurre el intervalo de tiempo de espera especificado, el subproceso entra en la cola de subprocesos listos.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)

Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelve a adquirir el bloqueo.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si transcurre el intervalo de tiempo de espera especificado, el subproceso entra en la cola de subprocesos listos.If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean) Wait(Object, Int32, Boolean)

Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelve a adquirir el bloqueo.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si transcurre el intervalo de tiempo de espera especificado, el subproceso entra en la cola de subprocesos listos.If the specified time-out interval elapses, the thread enters the ready queue. Este método también especifica si el dominio de sincronización del contexto (si se trata de un contexto sincronizado) sale antes de la espera y vuelve a adquirir el bloqueo después.This method also specifies whether the synchronization domain for the context (if in a synchronized context) is exited before the wait and reacquired afterward.

Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean) Wait(Object, TimeSpan, Boolean)

Libera el bloqueo en un objeto y bloquea el subproceso actual hasta que vuelve a adquirir el bloqueo.Releases the lock on an object and blocks the current thread until it reacquires the lock. Si transcurre el intervalo de tiempo de espera especificado, el subproceso entra en la cola de subprocesos listos.If the specified time-out interval elapses, the thread enters the ready queue. De modo opcional, sale del dominio de sincronización del contexto sincronizado antes de la espera y vuelve a adquirir el dominio después.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

Se aplica a

Seguridad para subprocesos

Este tipo es seguro para la ejecución de subprocesos.This type is thread safe.

Consulte también: