Monitor Třída

Definice

Poskytuje mechanismus, který synchronizuje přístup k objektům.Provides a mechanism that synchronizes access to objects.

public ref class Monitor abstract sealed
public ref class Monitor sealed
public static class Monitor
public sealed class Monitor
[System.Runtime.InteropServices.ComVisible(true)]
public static class Monitor
type Monitor = class
Public Class Monitor
Public NotInheritable Class Monitor
Dědičnost
Monitor
Atributy

Příklady

Následující příklad používá třídu Monitor pro synchronizaci přístupu k jedné instanci generátoru náhodných čísel reprezentovaného Random třídou.The following example uses the Monitor class to synchronize access to a single instance of a random number generator represented by the Random class. Příklad vytvoří deset úkolů, z nichž každý provede asynchronní zpracování ve vlákně fondu vláken.The example creates ten tasks, each of which executes asynchronously on a thread pool thread. Každý úkol generuje 10 000 náhodných čísel, vypočítá jejich průměr a aktualizuje dvě proměnné na úrovni procedury, které udržují průběžný součet počtu vygenerovaných náhodných čísel a jejich součtu.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. Po provedení všech úloh se tyto dvě hodnoty použijí pro výpočet celkového průměru.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)

Vzhledem k tomu, že k nim lze získat přístup z jakékoli úlohy spuštěné ve vlákně fondu vláken, musí být také synchronizovány proměnné total a n.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. Pro tento účel se používá metoda Interlocked.Add.The Interlocked.Add method is used for this purpose.

Následující příklad ukazuje kombinované použití třídy Monitor (implementováno pomocí lock nebo SyncLock konstrukce jazyka), třídy Interlocked a třídy AutoResetEvent.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. Definuje dvě internal třídy (in C#) nebo Friend (v Visual Basic) třídy SyncResource a UnSyncResource, které v uvedeném pořadí poskytují synchronizovaný a nesynchronizovaný přístup k prostředku.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. Aby bylo zajištěno, že příklad ukazuje rozdíl mezi synchronizovaným a nesynchronizovaným přístupem (což může být případ, kdy se každé volání metody rychle dokončí), metoda zahrnuje náhodné zpoždění: pro vlákna, jejichž vlastnost Thread.ManagedThreadId je sudá, metoda volá Thread.Sleep, aby zavedla zpoždění 2 000 milisekund.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. Všimněte si, že vzhledem k tomu, že třída SyncResource není veřejná, žádný kód klienta nezíská zámek u synchronizovaného prostředku; interní třída přebírá zámek.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. To brání škodlivému kódu v přebírání zámku veřejného objektu.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.

Příklad definuje proměnnou, numOps, která definuje počet vláken, která se pokusí o přístup k prostředku.The example defines a variable, numOps, that defines the number of threads that will attempt to access the resource. Vlákno aplikace volá metodu ThreadPool.QueueUserWorkItem(WaitCallback) pro synchronizovanou a nesynchronizovaný přístup pětkrát za tento počet.The application thread calls the ThreadPool.QueueUserWorkItem(WaitCallback) method for synchronized and unsynchronized access five times each. Metoda ThreadPool.QueueUserWorkItem(WaitCallback) má jeden parametr, delegát, který nepřijímá žádné parametry a nevrací žádnou hodnotu.The ThreadPool.QueueUserWorkItem(WaitCallback) method has a single parameter, a delegate that accepts no parameters and returns no value. Pro synchronizovaný přístup vyvolá metodu SyncUpdateResource; pro nesynchronizovaný přístup vyvolá metodu UnSyncUpdateResource.For synchronized access, it invokes the SyncUpdateResource method; for unsynchronized access, it invokes the UnSyncUpdateResource method. Po každé sadě volání metody volá vlákno aplikace metodu AutoResetEvent. WaitOne , aby blokovala, dokud není signalizována instance AutoResetEvent.After each set of method calls, the application thread calls the AutoResetEvent.WaitOne method so that it blocks until the AutoResetEvent instance is signaled.

Každé volání metody SyncUpdateResource volá interní metodu SyncResource.Access a pak zavolá metodu Interlocked.Decrement pro snížení čítače numOps.Each call to the SyncUpdateResource method calls the internal SyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. Metoda Interlocked.Decrement slouží k odečtení čítače, protože v opačném případě nemůžete určit, že druhé vlákno bude mít přístup k hodnotě před tím, než byla snížena hodnota prvního vlákna, která byla uložena v proměnné.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. Když poslední synchronizovaný pracovní vlákno sníží čítač na nulu, což znamená, že všechna synchronizovaná vlákna dokončila přístup k prostředku, metoda SyncUpdateResource volá metodu EventWaitHandle.Set, která signalizuje, že hlavní vlákno pokračuje v provádění.When the last synchronized worker thread decrements the counter to zero, indicating that all synchronized threads have completed accessing the resource, the SyncUpdateResource method calls the EventWaitHandle.Set method, which signals the main thread to continue execution.

Každé volání metody UnSyncUpdateResource volá interní metodu UnSyncResource.Access a pak zavolá metodu Interlocked.Decrement pro snížení čítače numOps.Each call to the UnSyncUpdateResource method calls the internal UnSyncResource.Access method and then calls the Interlocked.Decrement method to decrement the numOps counter. Po opětovném použití Interlocked.Decrement metoda slouží k snížení čítače, aby se zajistilo, že druhý podproces nepřistupuje k hodnotě před tím, než je přiřazena hodnota prvního vlákna snížené k proměnné.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. Když poslední nesynchronizovaný pracovní vlákno sníží čítač na nulu, což značí, že žádná další nesynchronizovaná vlákna potřebují získat přístup k prostředku, metoda UnSyncUpdateResource volá metodu EventWaitHandle.Set, která signalizuje, že hlavní vlákno pokračuje v provádění.When the last unsynchronized worker thread decrements the counter to zero, indicating that no more unsynchronized threads need to access the resource, the UnSyncUpdateResource method calls the EventWaitHandle.Set method, which signals the main thread to continue execution.

Jak výstup z příkladu ukazuje, synchronizovaný přístup zajistí, že volající vlákno ukončí chráněný prostředek předtím, než k němu může přistupovat jiné vlákno; každé vlákno čeká na jeho předchůdce.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. Na druhé straně bez zámku, je volána metoda UnSyncResource.Access v pořadí, ve kterém se vlákna dostanou.On the other hand, without the lock, the UnSyncResource.Access method is called in the order in which threads reach it.

Poznámky

Třída Monitor umožňuje synchronizovat přístup k oblasti kódu tím, že zabere a uvolní zámek konkrétního objektu voláním metod Monitor.Enter, Monitor.TryEntera Monitor.Exit.The Monitor class allows you to synchronize access to a region of code by taking and releasing a lock on a particular object by calling the Monitor.Enter, Monitor.TryEnter, and Monitor.Exit methods. Zámky objektů poskytují možnost omezit přístup k bloku kódu, který se běžně nazývá kritická část.Object locks provide the ability to restrict access to a block of code, commonly called a critical section. I když vlákno vlastní zámek pro objekt, žádné jiné vlákno nemůže získat tento zámek.While a thread owns the lock for an object, no other thread can acquire that lock. Můžete také použít třídu Monitor, abyste zajistili, že žádné jiné vlákno nepovoluje přístup k části kódu aplikace spouštěné vlastníkem zámku, pokud jiné vlákno neprovádí kód pomocí jiného uzamčeného objektu.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.

V tomto článku:In this article:

Třída monitorování: přehled The Monitor class: An overview
Objekt zámku The lock object
Kritická část The critical section
Pulse, PulseAll a Wait Pulse, PulseAll, and Wait
Monitory a obslužné rutiny čekáníMonitors and wait handles

Třída monitorování: PřehledThe Monitor class: An overview

Monitor má následující funkce:Monitor has the following features:

  • Je spojen s objektem na vyžádání.It is associated with an object on demand.

  • Není svázána, což znamená, že může být volána přímo z libovolného kontextu.It is unbound, which means it can be called directly from any context.

  • Nelze vytvořit instanci třídy Monitor; metody třídy Monitor jsou všechny statické.An instance of the Monitor class cannot be created; the methods of the Monitor class are all static. Každá metoda je předána synchronizovanému objektu, který řídí přístup k důležité části.Each method is passed the synchronized object that controls access to the critical section.

Poznámka

Použijte třídu Monitor k uzamknutí objektů jiných než řetězců (to znamená odkazové typy jiné než String), nikoli typů hodnot.Use the Monitor class to lock objects other than strings (that is, reference types other than String), not value types. Podrobnosti naleznete v části přetížení metody Enter a objektu zámku dále v tomto článku.For details, see the overloads of the Enter method and The lock object section later in this article.

Následující tabulka popisuje akce, které je možné učinit vlákny, které přistupují k synchronizovaným objektům:The following table describes the actions that can be taken by threads that access synchronized objects:

AkceAction PopisDescription
Enter, TryEnterEnter, TryEnter Získá zámek pro objekt.Acquires a lock for an object. Tato akce také označuje začátek kritické části.This action also marks the beginning of a critical section. Žádné jiné vlákno nemůže vstoupit do kritické části, pokud neprovádí instrukce v sekci kritické pomocí jiného uzamčeného objektu.No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
Wait Uvolní zámek objektu, aby bylo možné ostatním vláknům uzamknout a přistupovat k objektu.Releases the lock on an object in order to permit other threads to lock and access the object. Volající vlákno čeká, zatímco jiné vlákno přistupuje k objektu.The calling thread waits while another thread accesses the object. Signály Pulse slouží k upozornění na čekací vlákna týkající se změn stavu objektu.Pulse signals are used to notify waiting threads about changes to an object's state.
Pulse (Signal), PulseAllPulse (signal), PulseAll Odesílá signál do jednoho nebo více čekajících vláken.Sends a signal to one or more waiting threads. Signál upozorní čekající vlákno na změnu stavu uzamčeného objektu a vlastník zámku je připraven k uvolnění zámku.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. Čeká se na vložení vlákna do připravené fronty objektu, aby mohl nakonec získat zámek pro objekt.The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Jakmile má vlákno zámek, může ověřit nový stav objektu a zjistit, zda byl dosažen požadovaný stav.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 Uvolní zámek objektu.Releases the lock on an object. Tato akce také označí konec kritického oddílu chráněného uzamčeným objektem.This action also marks the end of a critical section protected by the locked object.

Počínaje .NET Framework 4.NET Framework 4existují dvě sady přetížení pro metody Enter a TryEnter.Beginning with the .NET Framework 4.NET Framework 4, there are two sets of overloads for the Enter and TryEnter methods. Jedna sada přetížení má ref (in C#) nebo ByRef (v Visual Basic) Boolean parametr, který je atomicky nastaven na true, pokud je získán zámek, a to i v případě, že je při získání zámku vyvolána výjimka.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. Tato přetížení použijte v případě, že je důležité uvolnit zámek ve všech případech, a to i v případě, že prostředky, které zámek chrání, nemusí být v konzistentním stavu.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.

Objekt zámkuThe lock object

Třída monitorování se skládá z metod static ( C#in) nebo Shared (v Visual Basic), které pracují s objektem, který řídí přístup k důležité části.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. Pro každý synchronizovaný objekt jsou zachovány následující informace:The following information is maintained for each synchronized object:

  • Odkaz na vlákno, které v současné době zámek obsahuje.A reference to the thread that currently holds the lock.

  • Odkaz na frontu připravenou, která obsahuje vlákna, která jsou připravena k získání zámku.A reference to a ready queue, which contains the threads that are ready to obtain the lock.

  • Odkaz na čekající frontu, která obsahuje vlákna, která čekají na oznámení o změně stavu uzamčeného objektu.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 zamkne objekty (tj. typy odkazů), nikoli typy hodnot.Monitor locks objects (that is, reference types), not value types. I když lze typ hodnoty předat Enter a Exit, je zabalen samostatně pro každé volání.While you can pass a value type to Enter and Exit, it is boxed separately for each call. Vzhledem k tomu, že každé volání vytvoří samostatný objekt Enter nikdy neblokuje a kód, který se má chránit, není skutečně synchronizovaný.Since each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. Kromě toho objekt předaný Exit je jiný než objekt předaný do Enter, takže Monitor vyvolá SynchronizationLockException výjimku s chybovou metodou synchronizace objektů, která byla volána z nesynchronizovaného bloku kódu.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."

Následující příklad ukazuje tento problém.The following example illustrates this problem. Spustí deset úkolů, z nichž každý bude v režimu spánku až 250 milisekund.It launches ten tasks, each of which just sleeps for 250 milliseconds. Každý úkol pak aktualizuje proměnnou čítače nTasks, což má za cíl spočítat počet úkolů, které byly skutečně spuštěny a provedeny.Each task then updates a counter variable, nTasks, which is intended to count the number of tasks that actually launched and executed. Vzhledem k tomu, že nTasks je globální proměnná, kterou je možné aktualizovat více úlohami současně, monitor se používá k ochraně před souběžnou úpravou více úloh.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. Jak však výstup z příkladu ukazuje, každá z úkolů vyvolá výjimku SynchronizationLockException.However, as the output from the example shows, each of the tasks throws a SynchronizationLockException exception.

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

public class Example
{
   public static void Main()
   {

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

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

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

Každý úkol vyvolá výjimku SynchronizationLockException, protože proměnná nTasks je zabalena před voláním metody Monitor.Enter v jednotlivých úkolech.Each task throws a SynchronizationLockException exception because the nTasks variable is boxed before the call to the Monitor.Enter method in each task. Jinými slovy, každé volání metody je předáno samostatnou proměnnou, která je nezávislá na ostatních.In other words, each method call is passed a separate variable that is independent of the others. nTasks je v volání metody Monitor.Exit zabalen znovu.nTasks is boxed again in the call to the Monitor.Exit method. Znovu vytvoří deset nových zabalených proměnných, které jsou nezávisle na sobě navzájem, nTasksa deseti zabalené proměnné vytvořené při volání metody Monitor.Enter.Once again, this creates ten new boxed variables, which are independent of each other, nTasks, and the ten boxed variables created in the call to the Monitor.Enter method. Výjimka je vyvolána, protože náš kód se pokouší uvolnit zámek pro nově vytvořenou proměnnou, která nebyla dříve uzamčena.The exception is thrown, then, because our code is attempting to release a lock on a newly created variable that was not previously locked.

I když lze proměnnou hodnotový typ zadat před voláním Enter a Exit, jak je znázorněno v následujícím příkladu, a předat stejný zabalený objekt do obou metod, neexistuje žádná výhoda k tomu.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. Změny v nezabalené proměnné se neprojeví v zabaleném kopírování a neexistuje žádný způsob, jak změnit hodnotu v krabicovém kopírování.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.

Při výběru objektu, na kterém chcete provést synchronizaci, byste měli zamknout pouze privátní nebo interní objekty.When selecting an object on which to synchronize, you should lock only on private or internal objects. Uzamykání u externích objektů může způsobit zablokování, protože nesouvisející kód by mohl zvolit stejné objekty, které se mají uzamknout pro různé účely.Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Všimněte si, že je možné provést synchronizaci s objektem ve více doménách aplikace, pokud je objekt použitý pro zámek odvozen z MarshalByRefObject.Note that you can synchronize on an object in multiple application domains if the object used for the lock derives from MarshalByRefObject.

Oddíl CriticalThe critical section

Pomocí metod Enter a Exit můžete označit začátek a konec kritické části.Use the Enter and Exit methods to mark the beginning and end of a critical section.

Poznámka

Funkce poskytovaná metodami Enter a Exit je shodná s tím, že jsou poskytnuty příkazem Lock v C# a v příkazu SyncLock v Visual Basic, s tím rozdílem, že jazykové konstrukce zabalí metody přetížení Monitor.Enter(Object, Boolean) a Monitor.Exit metoda v 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 blok pro zajištění vydání monitorování.block to ensure that the monitor is released.

Pokud je oddíl Critical sada souvislých instrukcí, pak zámek získaný metodou Enter garantuje, že se uzavřený kód může spustit pouze v jednom vlákně s uzamčeným objektem.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. V takovém případě doporučujeme umístit kód do try bloku a umístit volání do metody Exit v bloku finally.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. Tím se zajistí, že se zámek uvolní i v případě, že dojde k výjimce.This ensures that the lock is released even if an exception occurs. Tento model znázorňuje následující fragment kódu.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

Toto zařízení se obvykle používá k synchronizaci přístupu ke statické nebo instanční metodě třídy.This facility is typically used to synchronize access to a static or instance method of a class.

Pokud kritická část zahrnuje celou metodu, lze zařízení uzamykání dosáhnout umístěním System.Runtime.CompilerServices.MethodImplAttribute na metodu a určením Synchronized hodnotu v konstruktoru 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. Při použití tohoto atributu nejsou volání metod Enter a Exit potřeba.When you use this attribute, the Enter and Exit method calls are not needed. Tento model ilustruje následující fragment kódu:The following code fragment illustrates this pattern:

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

Všimněte si, že atribut způsobí, že aktuální vlákno drží zámek, dokud metoda nevrátí hodnotu; Pokud může být zámek vydaný dříve, použijte třídu Monitor, C# příkaz Lock nebo příkaz Visual Basic SyncLock uvnitř metody namísto atributu.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.

I když je možné Enter a Exit příkazy, které zamkne a uvolní daný objekt pro různé hranice členů nebo třídy nebo obojí, tento postup se nedoporučuje.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 a WaitPulse, PulseAll, and Wait

Jakmile vlákno vlastní zámek a zadává kritickou část, kterou zámek chrání, může volat metody Monitor.Wait, Monitor.Pulsea Monitor.PulseAll.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.

Když vlákno, které drží zámek, Wait, zámek se uvolní a vlákno se přidá do čekající fronty synchronizovaného objektu.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. První vlákno ve frontě připraveno, pokud je, získá zámek a přejde do kritické části.The first thread in the ready queue, if any, acquires the lock and enters the critical section. Vlákno, které se nazývá Wait, je přesunuto z čekající fronty do připravené fronty, pokud je metoda Monitor.Pulse nebo Monitor.PulseAll volána vláknem, který drží zámek (aby se mohl přesunout, vlákno musí být v hlavičce čekání na frontu).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). Metoda Wait vrátí, když volající vlákno znovu získá zámek.The Wait method returns when the calling thread reacquires the lock.

Když vlákno, které drží zámek, Pulse, vlákno v hlavičce čekající fronty se přesune do připravené fronty.When the thread that holds the lock calls Pulse, the thread at the head of the waiting queue is moved to the ready queue. Volání metody PulseAll přesune všechna vlákna z čekající fronty do připravené fronty.The call to the PulseAll method moves all the threads from the waiting queue to the ready queue.

Monitory a obslužné rutiny čekáníMonitors and wait handles

Je důležité poznamenat rozdíl mezi použitím Monitor třídy a objektů WaitHandle.It is important to note the distinction between the use of the Monitor class and WaitHandle objects.

  • Třída Monitor je čistě spravovaná, plně přenosná a může být efektivnější z pohledu na požadavky na prostředky operačního systému.The Monitor class is purely managed, fully portable, and might be more efficient in terms of operating-system resource requirements.

  • objekty WaitHandle představují objekty, které čekají na operační systém, jsou užitečné pro synchronizaci mezi spravovaným a nespravovaným kódem a zpřístupňují některé pokročilé funkce operačního systému, jako je schopnost čekat na mnoho objektů najednou.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.

Vlastnosti

LockContentionCount

Získá počet kolizí při pokusu o získání zámku monitoru.Gets the number of times there was contention when trying to take the monitor's lock.

Metody

Enter(Object)

Získá výhradní zámek pro zadaný objekt.Acquires an exclusive lock on the specified object.

Enter(Object, Boolean)

Získá výhradní zámek na zadaném objektu a atomicky nastaví hodnotu, která indikuje, jestli se zámek povedl.Acquires an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

Exit(Object)

Uvolní exkluzivní zámek pro zadaný objekt.Releases an exclusive lock on the specified object.

IsEntered(Object)

Určuje, zda aktuální vlákno drží zámek u zadaného objektu.Determines whether the current thread holds the lock on the specified object.

Pulse(Object)

Upozorní vlákno v čekající frontě na změnu stavu uzamčeného objektu.Notifies a thread in the waiting queue of a change in the locked object's state.

PulseAll(Object)

Upozorní všechny čekající vlákna na změnu stavu objektu.Notifies all waiting threads of a change in the object's state.

TryEnter(Object)

Pokusí se získat výhradní zámek pro zadaný objekt.Attempts to acquire an exclusive lock on the specified object.

TryEnter(Object, Boolean)

Pokusí se získat výhradní zámek na zadaném objektu a atomicky nastaví hodnotu, která indikuje, jestli se zámek povedl.Attempts to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken.

TryEnter(Object, Int32)

Pokusy, po zadaném počtu milisekund pro získání výhradního zámku pro zadaný objekt.Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object.

TryEnter(Object, Int32, Boolean)

Pokusy, po zadaný počet milisekund pro získání výhradního zámku na zadaném objektu a atomicky nastaví hodnotu, která indikuje, jestli se zámek povedl.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)

Pokusy o zadanou dobu pro získání výhradního zámku na zadaném objektu.Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object.

TryEnter(Object, TimeSpan, Boolean)

Pokusy o zadanou dobu pro získání výhradního zámku na zadaném objektu a atomicky nastaví hodnotu, která označuje, zda byl zámek pořízen.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.

Wait(Object)

Uvolní zámek objektu a zablokuje aktuální vlákno, dokud znovu nezíská zámek.Releases the lock on an object and blocks the current thread until it reacquires the lock.

Wait(Object, Int32)

Uvolní zámek objektu a zablokuje aktuální vlákno, dokud znovu nezíská zámek.Releases the lock on an object and blocks the current thread until it reacquires the lock. Pokud uplyne stanovený časový limit, vlákno přejde do fronty připravené.If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, Int32, Boolean)

Uvolní zámek objektu a zablokuje aktuální vlákno, dokud znovu nezíská zámek.Releases the lock on an object and blocks the current thread until it reacquires the lock. Pokud uplyne stanovený časový limit, vlákno přejde do fronty připravené.If the specified time-out interval elapses, the thread enters the ready queue. Tato metoda také určuje, zda je synchronizační doména pro kontext (je-li v synchronizovaném kontextu) ukončena před čekáním a znovu znovu získána.This method also specifies whether the synchronization domain for the context (if in a synchronized context) is exited before the wait and reacquired afterward.

Wait(Object, TimeSpan)

Uvolní zámek objektu a zablokuje aktuální vlákno, dokud znovu nezíská zámek.Releases the lock on an object and blocks the current thread until it reacquires the lock. Pokud uplyne stanovený časový limit, vlákno přejde do fronty připravené.If the specified time-out interval elapses, the thread enters the ready queue.

Wait(Object, TimeSpan, Boolean)

Uvolní zámek objektu a zablokuje aktuální vlákno, dokud znovu nezíská zámek.Releases the lock on an object and blocks the current thread until it reacquires the lock. Pokud uplyne stanovený časový limit, vlákno přejde do fronty připravené.If the specified time-out interval elapses, the thread enters the ready queue. Volitelně ukončí synchronizační doménu pro synchronizovaný kontext před čekáním a znovu získá doménu.Optionally exits the synchronization domain for the synchronized context before the wait and reacquires the domain afterward.

Platí pro

Bezpečný přístup z více vláken

Tento typ je bezpečný pro přístup z více vláken.This type is thread safe.

Viz také