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 o Monitor classe para sincronizar o acesso a uma única instância de um gerador de número aleatório representado pelo Random 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 executa de forma assíncrona em um pool de threads.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Cada tarefa gera números aleatórios 10.000, calcula a média e atualiza as 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 tiverem executados, esses dois valores, em seguida, sã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)

Porque eles podem ser acessados de qualquer tarefa em execução em um pool de threads, acesso às variáveis total e n também devem ser sincronizados.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 do Monitor classe (implementado com o lock ou SyncLock constructo de linguagem), o Interlocked classe e o AutoResetEvent 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 dois internal (em c#) ou Friend (no Visual Basic), classes de SyncResource e UnSyncResource, que fornecem acesso sincronizado e não sincronizado para um recurso, respectivamente.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 ilustra a diferença entre o acesso sincronizado e não sincronizada (que pode ser o caso se cada chamada de método é concluída rapidamente), o método inclui um atraso aleatório: para threads cuja Thread.ManagedThreadId propriedade for par, o chamadas de método Thread.Sleep 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 o SyncResource classe não é público, nenhum código cliente usa um bloqueio no recurso sincronizado; a própria classe interna leva 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 evita código mal-intencionado tire 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 tentam 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 o ThreadPool.QueueUserWorkItem(WaitCallback) método sincronizadas e não sincronizada acessar cinco vezes cada uma.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 o acesso sincronizado, ele invoca o SyncUpdateResource método; para obter 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 AutoResetEvent.WaitOne , de modo que ele bloqueia até que o AutoResetEvent instância é 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 chamadas de método interno SyncResource.Access método e, em seguida, chama o Interlocked.Decrement método para diminuir o numOps 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 diminuir o contador, pois caso contrário, você não pode ter certeza de que um segundo thread acessará o valor antes que um thread de primeiro 's decrementado valor foi 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 a última sincronizado diminui de thread de trabalho o contador de zero, indicando que todos os threads de sincronizados concluiu o acesso ao recurso, o SyncUpdateResource chamadas de método a EventWaitHandle.Set método, que sinaliza o thread principal para continuar execuçã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 chamadas de método interno UnSyncResource.Access método e, em seguida, chama o Interlocked.Decrement método para diminuir o numOps 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, o Interlocked.Decrement método é usado para diminuir o contador para garantir que o valor não acesso a um segundo thread antes que o valor de diminuído primeiro do thread foi 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 não sincronizada diminui de thread de trabalho para o contador de zero, indicando que não mais não sincronizada threads precisam acessar o recurso, o UnSyncUpdateResource chamadas de método a 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 mostra a saída do exemplo, o acesso sincronizado garante que o thread de chamada sai antes que outro thread pode acessá-lo; o recurso protegido cada thread aguarda 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 bloqueio, o UnSyncResource.Access método é chamado na ordem na qual threads acessá-lo.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Comentários

O Monitor classe permite que você sincronizar o acesso a uma região de código utilizando e liberar um bloqueio em um determinado objeto chamando o Monitor.Enter, Monitor.TryEnter, e 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. Bloqueios de objeto fornecem a capacidade de restringir o acesso a um bloco de código, normalmente chamado de uma 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 o bloqueio.While a thread owns the lock for an object, no other thread can acquire that lock. Você também pode usar o Monitor classe para garantir que nenhum outro segmento tem permissão para acessar uma seção do aplicativo de código que está sendo executada pelo proprietário do bloqueio, a menos que o outro thread está 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 e PulseAll espera Pulse, PulseAll, and Wait
Monitores e identificadores de esperaMonitors and wait handles

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

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

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

  • Ele é desativado, 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 das Monitor classe não pode ser criado; os métodos do Monitor 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 sincronizado 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 o Monitor classe para objetos de bloqueio que não sejam cadeias de caracteres (ou seja, tipos de referência diferente de String), não os 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 o objeto de bloqueio seção mais adiante 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 executadas 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 ele está 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 para bloquear e acessar 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. Sinais de pulso são usados para notificar os threads em espera sobre alterações no estado de um objeto.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (signal), 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 de pronto do objeto para que, eventualmente, ele poderá 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 pode 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 protegido pelo objeto bloqueado.This action also marks the end of a critical section protected by the locked object.

Começando com o .NET Framework 4.NET Framework 4, há dois conjuntos de sobrecargas para os Enter e TryEnter métodos.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 (em c#) ou ByRef (no Visual Basic) Boolean parâmetro atomicamente é definido como true se o bloqueio é adquirido, mesmo se uma exceção for gerada 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 ele 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 c#) ou Shared (no Visual Basic) métodos 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 para o thread que atualmente mantém o bloqueio.A reference to the thread that currently holds the lock.

  • Uma referência a uma fila de 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 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.

Monitor bloqueios de objetos (ou seja, tipos de referência), não os tipos de valor.Monitor locks objects (that is, reference types), not value types. Enquanto você pode passar um tipo de valor para Enter e Exit, ele é convertido separadamente para cada chamada.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Uma vez que cada chamada cria um objeto separado, Enter nunca blocos e o código que ele está protegendo supostamente não está 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 lança SynchronizationLockException exceção com a mensagem "método de sincronização do objeto foi chamado de um bloco não sincronizado de código."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 iniciará dez tarefas, cada um dos quais apenas ficará suspenso por 250 milissegundos.It launches ten tasks, each of which just sleeps for 250 milliseconds. Cada tarefa, em seguida, atualiza uma variável de contador, nTasks, que destina-se para contar o número de tarefas que realmente iniciado e executado.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Porque nTasks é uma variável global que pode ser atualizada por várias tarefas simultaneamente, um monitor é usado para protegê-lo contra modificações simultâneas 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 lança um SynchronizationLockException 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 o nTasks variável é convertido antes de chamar o Monitor.Enter 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 é passada a uma variável separada que é independente dos outros.In other words, each method call is passed a separate variable that is independent of the others. nTasks é convertido 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 demarcadas variáveis, que são independentes umas das outras, nTasks, e os dez boxed variáveis criadas na chamada para o Monitor.Enter 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 é gerada, em seguida, porque o nosso código está tentando liberar um bloqueio em uma variável recém-criada que não foi 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.

Embora a caixa de uma variável de tipo de valor antes de chamar Enter e Exit, conforme mostrado no exemplo a seguir e passam o mesmo objeto demarcado para ambos os métodos, não há nenhuma 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 para a variável não demarcada não são refletidas na cópia demarcada e não é possível alterar o valor da cópia do box.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 a sincronização, você deve bloquear apenas em objetos privados ou internos.When selecting an object on which to synchronize, you should lock only on private or internal objects. Bloquear objetos externos pode resultar em deadlocks, porque o código relacionado pode escolher os mesmos objetos bloquear em 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 de MarshalByRefObject.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 o Enter e Exit métodos para marcar o início e término 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 e Exit métodos é idêntica àquela fornecida pela bloqueio instrução em c# e o SyncLock instrução no Visual Basic, exceto que o quebra automática de construções de linguagem a Monitor.Enter(Object, Boolean) sobrecarga de método e o Monitor.Exit método em um 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 bloco para garantir que o monitor seja liberado.block to ensure that the monitor is released.

Se a seção crítica é um conjunto de instruções contíguas, em seguida, o bloqueio adquirido pelo Enter método garante que apenas um único thread pode 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, é recomendável que você coloque esse código em um try bloquear 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 estático ou o método 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 se estende por todo um método, o recurso de bloqueio poderá ser feito colocando a System.Runtime.CompilerServices.MethodImplAttribute no método e especificando as Synchronized valor no construtor do 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, o Enter e Exit chamadas de método não são necessários.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 manter o bloqueio até que o método retorne; Se o bloqueio pode ser liberado antes, use o Monitor classe, o c# bloqueio instrução ou o Visual Basic SyncLock instrução 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 o Enter e Exit instruções que bloquear e liberar um determinado objeto para cruzar o membro ou os 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 e PulseAll esperaPulse, PulseAll, and Wait

Depois que um thread possui o bloqueio e ele entrou na seção crítica que protege o bloqueio, ele pode chamar o Monitor.Wait, Monitor.Pulse, e 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.

Quando o thread que mantém as chamadas de bloqueio Wait, o bloqueio será 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 de pronto, 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 é movido da fila de espera para a fila de pronto quando ambos os Monitor.Pulse (para ser movido, o thread deve ser no início da fila de espera) ou o Monitor.PulseAll método é chamado pelo thread que mantém o bloqueio.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. O Wait método é retornado quando o thread de chamada adquira o bloqueio novamente.The Wait method returns when the calling thread reacquires the lock.

Quando o thread que mantém as chamadas de bloqueio Pulse, o thread no início da fila de espera é movido para a fila de pronto.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 de fila de espera para a fila de pronto.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitores e identificadores de esperaMonitors and wait handles

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

  • O Monitor classe é totalmente gerenciada, totalmente portáteis e podem 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.

  • WaitHandle objetos representam objetos de espera do sistema operacional, são úteis para a sincronização entre código gerenciado e e expõem alguns recursos avançados do sistema operacional, como a capacidade de aguardar vários 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

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