Monitor Monitor Monitor Monitor Class

Definição

Fornece um mecanismo que sincroniza o acesso a 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
Herança
MonitorMonitorMonitorMonitor
Atributos

Exemplos

O exemplo a seguir usa Monitor a classe para sincronizar o acesso a uma única instância de um gerador de números aleatórios representado Random pela 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. O exemplo cria dez tarefas, cada uma delas executada de forma assíncrona em um thread do pool de threads.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Cada tarefa gera 10.000 números aleatórios, calcula sua média e atualiza duas variáveis de nível de procedimento que mantêm um total acumulado do número de números aleatórios gerados e sua soma.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. Depois que todas as tarefas forem executadas, esses dois valores serão usados para calcular a média geral.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)

Como eles podem ser acessados de qualquer tarefa em execução em um thread de pool de threads n , o acesso às variáveis total e também deve ser sincronizado.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. O Interlocked.Add método é usado para essa finalidade.The Interlocked.Add method is used for this purpose.

O exemplo a seguir demonstra o uso combinado da Monitor classe (implementada com lock a SyncLock construção de linguagem ou) Interlocked , a classe e AutoResetEvent a 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. Ele define duas internal (em C#) ou Friend (em Visual Basic) classes SyncResource e UnSyncResource, respectivamente, fornece acesso sincronizado e não sincronizado a um 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 garantir que o exemplo ilustre a diferença entre o acesso sincronizado e não sincronizado (que poderia ser o caso se cada chamada de método for concluída rapidamente), o método incluirá um atraso aleatório: Thread.ManagedThreadId para threads cuja propriedade é par, o chamadas Thread.Sleep de método para introduzir um atraso de 2.000 milissegundos.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. Observe que, como a SyncResource classe não é pública, nenhum código do cliente recebe um bloqueio no recurso sincronizado; a própria classe interna usa o bloqueio.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. Isso impede que o código mal-intencionado faça um bloqueio em um 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.

O exemplo define uma variável, numOps, que define o número de threads que tentarão acessar o recurso.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. O thread do aplicativo chama ThreadPool.QueueUserWorkItem(WaitCallback) o método de acesso sincronizado e não sincronizado cinco vezes cada.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. O ThreadPool.QueueUserWorkItem(WaitCallback) método tem um único parâmetro, um delegado que não aceita parâmetros e não retorna nenhum valor.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. Para acesso sincronizado, ele invoca o SyncUpdateResource método; para acesso não sincronizado, ele invoca o UnSyncUpdateResource método.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Depois de cada conjunto de chamadas de método, o thread do aplicativo chama o método AutoResetEvent. WaitOne para que ele AutoResetEvent seja bloqueado até que a instância seja sinalizada.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 chamada para o SyncUpdateResource método chama o método SyncResource.Access interno e, em seguida Interlocked.Decrement , chama o método numOps para diminuir o contador.Each call to the SyncUpdateResource method calls the internal SyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. O Interlocked.Decrement método é usado para decrementar o contador, porque caso contrário, não é possível ter certeza de que um segundo thread acessará o valor antes que o valor decrementado de um primeiro thread tenha sido armazenado na variável.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 o último thread de trabalho sincronizado diminui o contador para zero, indicando que todos os threads sincronizados concluíram o SyncUpdateResource acesso ao recurso EventWaitHandle.Set , o método chama o método, que sinaliza o thread principal para continuar chão.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 chamada para o UnSyncUpdateResource método chama o método UnSyncResource.Access interno e, em seguida Interlocked.Decrement , chama o método numOps para diminuir o contador.Each call to the UnSyncUpdateResource method calls the internal UnSyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. Mais uma vez, Interlocked.Decrement o método é usado para decrementar o contador para garantir que um segundo thread não acesse o valor antes que o valor decrementado de um primeiro thread tenha sido atribuído à variável.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 o último thread de trabalho não sincronizado diminui o contador para zero, indicando que nenhum thread não sincronizado precisa acessar o recurso, o UnSyncUpdateResource método chama o EventWaitHandle.Set método, que sinaliza o thread principal para continuar a execução .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 a saída do exemplo mostra, o acesso sincronizado garante que o thread de chamada saia do recurso protegido antes que outro thread possa acessá-lo; cada thread aguarda em seu antecessor.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 outro lado, sem o bloqueio, o UnSyncResource.Access método é chamado na ordem em que os threads chegam.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Comentários

A Monitor classe permite que você sincronize o acesso a uma região de código, realizando e liberando um bloqueio em um objeto específico Monitor.Enterchamando Monitor.TryEnteros métodos Monitor.Exit , e.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. Os bloqueios de objeto fornecem a capacidade de restringir o acesso a um bloco de código, normalmente chamado de seção crítica.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. Enquanto um thread possui o bloqueio de um objeto, nenhum outro thread pode adquirir esse bloqueio.While a thread owns the lock for an object, no other thread can acquire that lock. Você também pode usar a Monitor classe para garantir que nenhum outro thread tenha permissão para acessar uma seção do código do aplicativo que está sendo executado pelo proprietário do bloqueio, a menos que o outro thread esteja executando o código usando um objeto bloqueado diferente.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.

Neste artigo:In this article:

A classe monitor: Uma visão geral The Monitor class: An overview
O objeto de bloqueio The lock object
A seção crítica The critical section
Pulse, PulseAll e Wait Pulse, PulseAll, and Wait
Monitores e espera de identificadoresMonitors and wait handles

A classe monitor: Uma visão geralThe Monitor class: An overview

Monitoro tem os seguintes recursos:Monitor has the following features:

  • Ele está associado a um objeto sob demanda.It is associated with an object on demand.

  • Ele está desassociado, o que significa que ele pode ser chamado diretamente de qualquer contexto.It is unbound, which means it can be called directly from any context.

  • Uma instância da Monitor classe não pode ser criada; os métodos Monitor da classe são todos estáticos.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. Cada método recebe o objeto Synchronized que controla o acesso à seção crítica.Each method is passed the synchronized object that controls access to the critical section.

Observação

Use a Monitor classe para bloquear objetos diferentes de cadeias de caracteres (ou seja, tipos Stringde referência diferentes de), não tipos de valor.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Para obter detalhes, consulte as sobrecargas do Enter método e a seção bloquear objeto posteriormente neste artigo.For details, see the overloads of the Enter method and The lock object section later in this article.

A tabela a seguir descreve as ações que podem ser tomadas por threads que acessam objetos sincronizados:The following table describes the actions that can be taken by threads that access synchronized objects:

AçãoAction DescriçãoDescription
Enter, TryEnterEnter, TryEnter Adquire um bloqueio para um objeto.Acquires a lock for an object. Essa ação também marca o início de uma seção crítica.This action also marks the beginning of a critical section. Nenhum outro thread pode inserir a seção crítica, a menos que esteja executando as instruções na seção crítica usando um objeto bloqueado diferente.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 o bloqueio em um objeto para permitir que outros threads bloqueiem e acessem o objeto.Releases the lock on an object in order to permit other threads to lock and access the object. O thread de chamada aguarda enquanto outro thread acessa o objeto.The calling thread waits while another thread accesses the object. Os sinais de pulso são usados para notificar os threads em espera sobre as alterações no estado de um objeto.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse(sinal),PulseAllPulse (signal), PulseAll Envia um sinal para um ou mais threads em espera.Sends a signal to one or more waiting threads. O sinal notifica um thread em espera que o estado do objeto bloqueado foi alterado e o proprietário do bloqueio está pronto para liberar o bloqueio.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. O thread em espera é colocado na fila pronta do objeto para que ele possa eventualmente receber o bloqueio do objeto.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Depois que o thread tiver o bloqueio, ele poderá verificar o novo estado do objeto para ver se o estado necessário foi atingido.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 o bloqueio em um objeto.Releases the lock on an object. Essa ação também marca o final de uma seção crítica protegida pelo objeto bloqueado.This action also marks the end of a critical section protected by the locked object.

A partir do .NET Framework 4.NET Framework 4, há dois conjuntos de sobrecargas para os Enter métodos e TryEnter .Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Um conjunto de sobrecargas tem um ref parâmetro ( C#in) ByRef ou (no Visual Basic Boolean ) que é definido atomicamente para true se o bloqueio for adquirido, mesmo se uma exceção for lançada ao adquirir o bloqueio.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 essas sobrecargas se for essencial para liberar o bloqueio em todos os casos, mesmo quando os recursos que o bloqueio está protegendo podem não estar em um estado consistente.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.

O objeto de bloqueioThe lock object

A classe monitor consiste static em métodos (in C#) Shared ou (em Visual Basic) que operam em um objeto que controla o acesso à seção 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. As informações a seguir são mantidas para cada objeto sincronizado:The following information is maintained for each synchronized object:

  • Uma referência ao thread que contém o bloqueio no momento.A reference to the thread that currently holds the lock.

  • Uma referência a uma fila pronta, que contém os threads que estão prontos para obter o bloqueio.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Uma referência a uma fila de espera, que contém os threads que estão aguardando a notificação de uma alteração no estado do 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.

Monitorbloqueia objetos (ou seja, tipos de referência), não tipos de valor.Monitor locks objects (that is, reference types), not value types. Embora você possa passar um tipo de valor Enter para Exite, ele é emoldurado separadamente para cada chamada.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Como cada chamada cria um objeto separado, Enter nunca bloqueia, e o código que ele está supostamente protegendo não é realmente sincronizado.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. Além disso, o objeto passado para Exit é diferente do objeto passado para Enter, então Monitor gera SynchronizationLockException uma exceção com a mensagem "o método de sincronização de objeto foi chamado a partir de um bloco de código não sincronizado".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."

O exemplo a seguir ilustra esse problema.The following example illustrates this problem. Ele inicia dez tarefas, cada uma das quais apenas dorme por 250 milissegundos.It launches ten tasks, each of which just sleeps for 250 milliseconds. Em seguida, cada tarefa atualiza uma variável nTasksde contador,, que é destinada a contar o número de tarefas que realmente são iniciadas e executadas.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Como nTasks é uma variável global que pode ser atualizada por várias tarefas simultaneamente, um monitor é usado para protegê-la de uma modificação simultânea por várias tarefas.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. No entanto, como a saída do exemplo mostra, cada uma das tarefas gera SynchronizationLockException uma exceção.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 tarefa gera uma SynchronizationLockException exceção porque a nTasks variável é encaixada antes da chamada Monitor.Enter para o método em cada tarefa.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. Em outras palavras, cada chamada de método recebe uma variável separada que é independente das outras.In other words, each method call is passed a separate variable that is independent of the others. nTasksestá em caixa novamente na chamada para o Monitor.Exit método.nTasks is boxed again in the call to the Monitor.Exit method. Mais uma vez, isso cria dez novas variáveis em caixa, que são independentes umas das nTasksoutras, e as dez variáveis in a box criadas na chamada Monitor.Enter para o método.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 exceção é lançada, então, porque nosso código está tentando liberar um bloqueio em uma variável recém-criada que não foi bloqueada 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.

Embora você possa usar uma variável de tipo Value antes Enter de Exitchamar e, conforme mostrado no exemplo a seguir, e passar o mesmo objeto Boxed para ambos os métodos, não há vantagem em fazer isso.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. As alterações na variável desemoldurada não são refletidas na cópia em caixa, e não há como alterar o valor da cópia em caixa.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.

Ao selecionar um objeto no qual sincronizar, você deve bloquear somente em objetos privados ou internos.When selecting an object on which to synchronize, you should lock only on private or internal objects. O bloqueio de objetos externos pode resultar em deadlocks, porque o código não relacionado pode escolher os mesmos objetos a serem bloqueados para finalidades diferentes.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Observe que você pode sincronizar em um objeto em vários domínios de aplicativo se o objeto usado para o bloqueio for derivado MarshalByRefObjectde.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

A seção críticaThe critical section

Use os Enter métodos Exit e para marcar o início e o fim de uma seção crítica.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Observação

A funcionalidade fornecida pelos Enter métodos e Exit é idêntica à fornecida pela instrução Lock no C# e a instrução SyncLock no Visual Basic, exceto que as construções de linguagem encapsulam o Monitor.Enter(Object, Boolean)sobrecarga de método e Monitor.Exit o método em tryum...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 Bloquear para garantir que o monitor seja liberado.block to ensure that the monitor is released.

Se a seção crítica for um conjunto de instruções contíguas, o bloqueio adquirido pelo Enter método garante que apenas um único thread possa executar o código incluído com o 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. Nesse caso, recomendamos que você coloque esse código em um try bloco e coloque a chamada para o Exit método em um finally bloco.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. Isso garante que o bloqueio seja liberado mesmo se ocorrer uma exceção.This ensures that the lock is released even if an exception occurs. O fragmento de código a seguir ilustra esse padrão.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

Esse recurso é normalmente usado para sincronizar o acesso a um método estático ou de instância de uma classe.This facility is typically used to synchronize access to a static or instance method of a class.

Se uma seção crítica abranger um método inteiro, o recurso de bloqueio poderá ser obtido colocando o System.Runtime.CompilerServices.MethodImplAttribute no método e especificando o Synchronized valor no construtor 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. Quando você usa esse atributo, as Enter chamadas Exit de método e não são necessárias.When you use this attribute, the Enter and Exit method calls are not needed. O fragmento de código a seguir ilustra esse padrão:The following code fragment illustrates this pattern:

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

Observe que o atributo faz com que o thread atual Mantenha o bloqueio até que o método retorne; Se o bloqueio puder ser liberado antes, use a Monitor classe, a C# instrução Lock ou a instrução Visual Basic SyncLock dentro do método em vez do 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.

Embora seja possível para as Enter instruções e Exit que bloqueiam e liberam um determinado objeto para cruzar Membros ou limites de classe ou ambos, essa prática não é recomendada.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

Depois que um thread possui o bloqueio e inseriu a seção crítica que o bloqueio protege, ele pode chamar Monitor.Waitos Monitor.Pulsemétodos, 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 o thread que mantém as chamadas Waitde bloqueio, o bloqueio é liberado e o thread é adicionado à fila de espera do 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. O primeiro thread na fila pronta, se houver, adquire o bloqueio e entra na seção crítica.The first thread in the ready queue, if any, acquires the lock and enters the critical section. O thread que chamou Wait é movido da fila de espera para a fila pronta quando o Monitor.Pulse ou o Monitor.PulseAll método é chamado pelo thread que mantém o bloqueio (a ser movido, o thread deve estar no início da fila de espera).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). O Wait método retorna quando o thread de chamada readquire o bloqueio.The Wait method returns when the calling thread reacquires the lock.

Quando o thread que mantém as chamadas Pulsede bloqueio, o thread no início da fila de espera é movido para a fila pronta.When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. A chamada para o PulseAll método move todos os threads da fila de espera para a fila pronta.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitores e espera de identificadoresMonitors and wait handles

É importante observar a distinção entre o uso da Monitor classe e WaitHandle dos objetos.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • A Monitor classe é puramente gerenciada, totalmente portátil e pode ser mais eficiente em termos de requisitos de recursos do sistema operacional.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • WaitHandleos objetos representam objetos que podem ser esperados do sistema operacional, são úteis para a sincronização entre códigos gerenciados e não gerenciados e expõem alguns recursos avançados do sistema operacional, como a capacidade de aguardar muitos objetos ao mesmo tempo.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.

Propriedades

LockContentionCount LockContentionCount LockContentionCount LockContentionCount

Obtém o número de vezes em que houve contenção ao tentar realizar o bloqueio do monitor.Gets the number of times there was contention when trying to take the monitor's lock.

Métodos

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

Adquire um bloqueio exclusivo no objeto especificado.Acquires an exclusive lock on the specified object.

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

Obtém um bloqueio exclusivo no objeto especificado e define automaticamente um valor que indica se o bloqueio foi realizado.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 um bloqueio exclusivo no objeto especificado.Releases an exclusive lock on the specified object.

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

Determina se o thread atual mantém o bloqueio no objeto especificado.Determines whether the current thread holds the lock on the specified object.

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

Notifica um thread na fila de espera de uma alteração no estado do objeto bloqueado.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 todos os threads em espera de uma alteração no estado do objeto.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)

Tenta, pelo período especificado, obter um bloqueio exclusivo no objeto especificado e define automaticamente um valor que indica se o bloqueio foi realizado.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)

Tenta, pelo número de milissegundos especificado, obter um bloqueio exclusivo no objeto especificado e define de forma atômica um valor que indica se o bloqueio foi realizado.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)

Tentativas, durante o período especificado de tempo, para adquirir um bloqueio exclusivo no objeto especificado.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)

Tenta obter um bloqueio exclusivo no objeto especificado e define automaticamente um valor que indica se o bloqueio foi realizado.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)

Tenta adquirir um bloqueio exclusivo no objeto especificado.Attempts to acquire an exclusive lock on the specified object.

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

Tentativas, durante o período especificado de milésimos de segundos, para adquirir um bloqueio exclusivo no objeto especificado.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)

Libera o bloqueio de um objeto e bloqueia o thread atual até que ele adquira o bloqueio novamente.Releases the lock on an object and blocks the current thread until it reacquires the lock. Se o intervalo de tempo limite especificado transcorrer, o thread entrará na fila pronta.If the specified time-out interval elapses, the thread enters the ready queue. Esse método também especifica se o domínio de sincronização para o contexto (se estiver em um contexto sincronizado) é encerrado antes do tempo de espera e readquirido posteriormente.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)

Libera o bloqueio de um objeto e bloqueia o thread atual até que ele adquira o bloqueio novamente.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 o bloqueio de um objeto e bloqueia o thread atual até que ele adquira o bloqueio novamente.Releases the lock on an object and blocks the current thread until it reacquires the lock. Se o intervalo de tempo limite especificado transcorrer, o thread entrará na fila pronta.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 o bloqueio de um objeto e bloqueia o thread atual até que ele adquira o bloqueio novamente.Releases the lock on an object and blocks the current thread until it reacquires the lock. Se o intervalo de tempo limite especificado transcorrer, o thread entrará na fila pronta.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)

Libera o bloqueio de um objeto e bloqueia o thread atual até que ele adquira o bloqueio novamente.Releases the lock on an object and blocks the current thread until it reacquires the lock. Se o intervalo de tempo limite especificado transcorrer, o thread entrará na fila pronta.If the specified time-out interval elapses, the thread enters the ready queue. Opcionalmente, encerra o domínio de sincronização para o contexto sincronizado antes do tempo de espera e, depois disso, readquire o domínio.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

Aplica-se a

Acesso thread-safe

Este tipo é thread-safe.This type is thread safe.

Veja também