Megosztás a következőn keresztül:


System.Threading.Monitor osztály

Ez a cikk kiegészítő megjegyzéseket tartalmaz az API referenciadokumentációjához.

Az Monitor osztály lehetővé teszi a kód egy régiójához való hozzáférés szinkronizálását egy adott objektum zárolásának felvételével és felszabadításával a Monitor.Enter, Monitor.TryEnterés Monitor.Exit metódusok meghívásával. Az objektumzárak lehetővé teszik a kódblokkokhoz való hozzáférés korlátozását, amelyet gyakran kritikus szakasznak neveznek. Bár egy szál egy objektum zárolását birtokolja, más szál nem szerezheti be ezt a zárolást. Az osztály használatával Monitor azt is biztosíthatja, hogy más szál ne férhessen hozzá a zárolástulajdonos által végrehajtott alkalmazáskód egy szakaszához, kivéve, ha a másik szál egy másik zárolt objektummal hajtja végre a kódot. Mivel a Monitor osztály szál affinitással rendelkezik, a zárolást beszerző szálnak fel kell szabadítania a zárolást a Monitor.Exit metódus meghívásával.

Áttekintés

Monitor a következő funkciókkal rendelkezik:

  • Igény szerinti objektumhoz van társítva.
  • Ez kötetlen, ami azt jelenti, hogy közvetlenül bármely környezetből meghívható.
  • Az osztály egy példánya Monitor nem hozható létre; az Monitor osztály metódusai mind statikusak. A rendszer minden metódust átad a kritikus szakaszhoz való hozzáférést vezérlő szinkronizált objektumnak.

Feljegyzés

Az osztály használatával a Monitor sztringeken kívül más objektumokat (azaz nem hivatkozástípusokat String) zárolhat, nem értéktípusokat. További részletekért tekintse meg a Enter metódus túlterheléseit és a zárolási objektum szakaszt a cikk későbbi részében.

Az alábbi táblázat a szinkronizált objektumokhoz hozzáférő szálak által végrehajtható műveleteket ismerteti:

Művelet Leírás
Enter, TryEnter Egy objektum zárolását szerzi be. Ez a művelet egy kritikus szakasz kezdetét is jelzi. Más szál csak akkor léphet be a kritikus szakaszba, ha a kritikus szakaszban lévő utasításokat egy másik zárolt objektummal hajtja végre.
Wait Feloldja a zárolást egy objektumon, hogy más szálak zárolhassák és elérhessék az objektumot. A hívószál megvárja, amíg egy másik szál hozzáfér az objektumhoz. A rendszer impulzusjelekkel értesíti a várakozó szálakat az objektum állapotának változásairól.
Pulse (jel), PulseAll Jelet küld egy vagy több várakozó szálnak. A jel értesíti a várakozó szálat, hogy a zárolt objektum állapota megváltozott, és a zárolás tulajdonosa készen áll a zárolás feloldására. A várakozási szál az objektum kész üzenetsorába kerül, hogy végül megkapja az objektum zárolását. Miután a szál megkapta a zárolást, ellenőrizheti az objektum új állapotát, és ellenőrizheti, hogy elérte-e a szükséges állapotot.
Exit Feloldja a zárolást egy objektumon. Ez a művelet a zárolt objektum által védett kritikus szakasz végét is jelzi.

Két túlterhelési csoport van a és TryEnter a Enter módszerek számára. Egy túlterhelési csoport rendelkezik egy ref (C#-ban) vagy ByRef (Visual Basicben) Boolean paraméterrel, amely atomilag a zárolás megszerzésekor van beállítva true , még akkor is, ha a zárolás beszerzésekor kivétel történik. Ezeket a túlterheléseket akkor használja, ha minden esetben kritikus fontosságú a zárolás feloldása, még akkor is, ha a zárolás által védett erőforrások nem konzisztens állapotban vannak.

A zárolási objektum

A Monitor osztály (SharedVisual Basic) metódusokból áll static , amelyek egy olyan objektumon működnek, amely szabályozza a kritikus szakaszhoz való hozzáférést. Az egyes szinkronizált objektumok esetében a következő információk maradnak fenn:

  • A zárolást jelenleg tároló szálra mutató hivatkozás.
  • Egy kész üzenetsorra mutató hivatkozás, amely tartalmazza azokat a szálakat, amelyek készen állnak a zárolás beszerzésére.
  • Egy várakozási üzenetsorra mutató hivatkozás, amely tartalmazza azokat a szálakat, amelyek a zárolt objektum állapotának változására várnak.

Monitor objektumokat (azaz hivatkozástípusokat) zárol, nem értéktípusokat. Bár átadhat egy értéktípust Enter , az Exitegyes hívásokhoz külön van bekeretezve. Mivel minden hívás külön objektumot hoz létre, Enter soha nem blokkolja a kódot, amelyet állítólag véd, nem szinkronizálódik. Emellett az átadott Exit objektum eltér az átadott Enterobjektumtól, ezért Monitor kivételt SynchronizationLockException jelez az "Objektumszinkronizálási módszer meghívása nem aszinkron kódblokkból" üzenettel.

Az alábbi példa ezt a problémát szemlélteti. Tíz feladatot indít el, amelyek mindegyike csak 250 ezredmásodpercig alszik. Minden tevékenység ezután frissít egy számlálóváltozót, nTasksamely a ténylegesen elindított és végrehajtott tevékenységek számának megszámlálására szolgál. Mivel nTasks egy olyan globális változó, amelyet egyszerre több tevékenység is frissíthet, a figyelő több tevékenység egyidejű módosításával szembeni védelmére szolgál. Ahogy azonban a példa kimenete is mutatja, az egyes tevékenységek kivételt SynchronizationLockException képeznek.

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

public class Example1
{
    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 Example3
    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.

Minden tevékenység kivételt SynchronizationLockException jelez, mert a nTasks változó az egyes tevékenységek metódusának Monitor.Enter hívása előtt be van jelölve. Más szóval minden metódushívás egy külön változót ad át, amely független a többiekétől. nTasks a metódus hívásában Monitor.Exit ismét be van jelölve. Ez ismét létrehoz tíz új, egymástól független dobozos változót, nTasksvalamint a metódus hívásában Monitor.Enter létrehozott tíz dobozos változót. A kivételt akkor a rendszer kibocsátja, mert a kód egy korábban nem zárolt, újonnan létrehozott változó zárolását próbálja feloldani.

Bár meghívhat egy értéktípus-változót Enter , és Exitaz alábbi példában látható módon mindkét metódusnak átadhatja ugyanazt a dobozos objektumot, ennek nincs előnye. A nem beérkezett változó módosításai nem jelennek meg a dobozos másolatban, és nincs mód a dobozos másolat értékének módosítására.

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 Example2
    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.

Ha kiválaszt egy objektumot, amelyen szinkronizálni szeretne, csak privát vagy belső objektumokra kell zárolnia. A külső objektumok zárolása holtpontot eredményezhet, mivel a nem kapcsolódó kód ugyanazokat az objektumokat választhatja ki, amelyek különböző célokra zárolhatók.

Vegye figyelembe, hogy több alkalmazástartományban szinkronizálhat egy objektumot, ha a zároláshoz használt objektum származik.MarshalByRefObject

A kritikus szakasz

Enter A kritikus szakasz elejét és Exit végét a metódusokkal jelölhet meg.

Feljegyzés

Az és a Enter metódusok által biztosított funkciók megegyeznek a C# zárolási utasítása és a Visual Basic SyncLock utasítása által biztosított funkciókkal, azzal a kivétellel, hogy a nyelvi szerkezetek a Monitor.Enter(Object, Boolean) metódus túlterhelését és a Monitor.Exit metódust tryExit egy ...finally blokkot a monitor kiadásának biztosításához.

Ha a kritikus szakasz egy összefüggő utasítások halmaza, akkor a metódus által Enter beszerzett zárolás garantálja, hogy csak egyetlen szál tudja végrehajtani a zárt kódot a zárolt objektummal. Ebben az esetben azt javasoljuk, hogy a kódot egy try blokkba helyezze, és helyezze el a metódus meghívását Exit egy finally blokkban. Ez biztosítja, hogy a zárolás kivétel esetén is feloldva legyen. A következő kódrészlet ezt a mintát szemlélteti.

// 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

Ezt a létesítményt általában egy osztály statikus vagy példánymetódusához való hozzáférés szinkronizálására használják.

Ha egy kritikus szakasz egy teljes metódusra terjed ki, a zárolási létesítmény a metóduson való elhelyezéssel System.Runtime.CompilerServices.MethodImplAttribute és a Synchronized konstruktor értékének System.Runtime.CompilerServices.MethodImplAttributemegadásával érhető el. Ha ezt az attribútumot használja, a Enter metódushívásokra nincs Exit szükség. A következő kódrészlet a következő mintát szemlélteti:

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

Vegye figyelembe, hogy az attribútum miatt az aktuális szál addig tartja a zárolást, amíg a metódus vissza nem tér; ha a zárolás hamarabb feloldható, használja az Monitor osztályt, a C# zárolási utasítást vagy a Visual Basic SyncLock utasítást a metóduson belül az attribútum helyett.

Bár lehetséges, hogy az Enter adott objektumot zároló és Exit felszabadító utasítások egy adott objektumot tag- vagy osztályhatárok vagy mindkettő között zárolnak és engedjenek fel, ez a gyakorlat nem ajánlott.

Pulse, PulseAll és Wait

Miután egy szál birtokolja a zárolást, és belépett a zárolás által védett kritikus szakaszba, meghívhatja a Monitor.Wait, Monitor.Pulseés Monitor.PulseAll metódusokat.

A zárolási hívásokat Waittartalmazó szál feloldja a zárolást, és hozzáadja a szálat a szinkronizált objektum várólistájára. Ha van ilyen, a kész üzenetsor első szála beszerzi a zárolást, és belép a kritikus szakaszba. A hívott Wait szál a várakozási sorból a kész üzenetsorba kerül, amikor a Monitor.Pulse zárolást tartalmazó szál meghívja vagy Monitor.PulseAll meghívja a metódust (az áthelyezendő szálnak a várakozási várólista élén kell lennie). A Wait metódus akkor ad vissza, amikor a hívószál újból zárolja a zárolást.

Amikor a zárolási hívásokat Pulsetároló szál a várakozási sor elején lévő szálat a kész üzenetsorba helyezi át. A metódus hívása PulseAll az összes szálat áthelyezi a várakozási sorból a kész üzenetsorba.

Monitorozási és várakozási fogópontok

Fontos megjegyezni az osztály és WaitHandle az Monitor objektumok használata közötti különbséget.

  • Az Monitor osztály tisztán felügyelt, teljes mértékben hordozható, és az operációs rendszer erőforrásigényét tekintve hatékonyabb lehet.
  • WaitHandle Az objektumok az operációs rendszer várakozásra váró objektumait jelölik, hasznosak a felügyelt és a nem felügyelt kód közötti szinkronizáláshoz, és elérhetővé tehetnek néhány speciális operációsrendszer-funkciót, például azt, hogy egyszerre több objektumon is várakozhat.

Példák

Az alábbi példa az osztály használatával szinkronizálja a Monitor hozzáférést az osztály által képviselt véletlenszerű számgenerátor egyetlen példányához Random . A példa tíz feladatot hoz létre, amelyek mindegyike aszinkron módon hajt végre egy szálkészlet-szálon. Minden tevékenység 10 000 véletlenszerű számot hoz létre, kiszámítja az átlagot, és frissíti a két eljárásszintű változót, amelyek a létrehozott véletlenszerű számok és az összegük futó összegét tartják fenn. Az összes tevékenység végrehajtása után a rendszer ezt a két értéket használja a teljes középérték kiszámításához.

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

public class Example2
{
    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 Example4
    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)

Mivel a szálkészlet-szálon futó bármely tevékenységből elérhetők, a változókhoz totaln való hozzáférést is szinkronizálni kell. Erre Interlocked.Add a célra a metódust használják.

Az alábbi példa az osztály (vagy SyncLock nyelvi szerkezettel implementált) együttes használatát Monitor mutatja be, az Interlocked osztályt és az osztályt AutoResetEventlock. Két internal (C#-ban) vagy Friend (Visual Basic)-osztályt határoz meg, SyncResource amelyek UnSyncResourceszinkronizált és nem aszinkron hozzáférést biztosítanak egy erőforráshoz. Annak érdekében, hogy a példa bemutassa a szinkronizált és a nem aszinkron hozzáférés közötti különbséget (ami akkor fordulhat elő, ha az egyes metódushívások gyorsan befejeződnek), a metódus véletlenszerű késleltetést tartalmaz: olyan szálak esetében, amelyek Thread.ManagedThreadId tulajdonsága egyenlő, a metódus 2000 ezredmásodperc késleltetést hív Thread.Sleep meg. Vegye figyelembe, hogy mivel az SyncResource osztály nem nyilvános, egyik ügyfélkód sem zárolja a szinkronizált erőforrást; maga a belső osztály veszi át a zárolást. Ez megakadályozza, hogy a rosszindulatú kód zároljon egy nyilvános objektumot.

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 + Environment.NewLine + "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 + Environment.NewLine + "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.

A példa egy változót határoz meg, numOpsamely meghatározza az erőforrás eléréséhez megkísérlendő szálak számát. Az alkalmazáslánc ötször hívja meg a ThreadPool.QueueUserWorkItem(WaitCallback) szinkronizált és a nem szinkronizált hozzáférés metódusát. A ThreadPool.QueueUserWorkItem(WaitCallback) metódus egyetlen paraméterrel rendelkezik, egy meghatalmazott, amely nem fogad el paramétereket, és nem ad vissza értéket. Szinkronizált hozzáférés esetén meghívja a SyncUpdateResource metódust; a nem aszinkron hozzáférés esetén meghívja a metódust UnSyncUpdateResource . Minden egyes metódushívás után az alkalmazásszál meghívja az AutoResetEvent.WaitOne metódust, hogy blokkolja a AutoResetEvent példány jelzéséig.

A metódus minden egyes hívása meghívja a SyncUpdateResource belső SyncResource.Access metódust, majd meghívja a metódust a Interlocked.Decrement számláló elutasítására numOps . A Interlocked.Decrement metódus a számláló dekrementálására szolgál, mert ellenkező esetben nem lehet biztos abban, hogy egy második szál hozzáfér az értékhez, mielőtt egy első szál dekrementált értékét tárolnák a változóban. Amikor az utolsó szinkronizált feldolgozószál nullára állítja a számlálót, ami azt jelzi, hogy az összes szinkronizált szál hozzáfért az erőforráshoz, a SyncUpdateResource metódus meghívja a EventWaitHandle.Set metódust, amely a fő szálat a végrehajtás folytatására jelzi.

A metódus minden egyes hívása meghívja a UnSyncUpdateResource belső UnSyncResource.Access metódust, majd meghívja a metódust a Interlocked.Decrement számláló elutasítására numOps . A Számláló ismét a Interlocked.Decrement számláló dekrementálására szolgál, hogy a második szál ne férhessen hozzá az értékhez, mielőtt az első szál decrementált értékét hozzárendelték volna a változóhoz. Amikor az utolsó nem aszinkron munkaszál nullára állítja a számlálót, ami azt jelzi, hogy nincs szükség több nem szinkronizált szálra az erőforrás eléréséhez, a UnSyncUpdateResource metódus meghívja a EventWaitHandle.Set metódust, amely a fő szálat jelzi a végrehajtás folytatásához.

Ahogy a példa kimenete is mutatja, a szinkronizált hozzáférés biztosítja, hogy a hívószál kilépjen a védett erőforrásból, mielőtt egy másik szál hozzáférhet hozzá; minden szál várakozik az elődjén. Másrészt a zárolás nélkül a UnSyncResource.Access metódust abban a sorrendben hívjuk meg, amelyben a szálak elérik.