Bagikan melalui


Kelas System.Threading.Monitor

Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.

Kelas ini Monitor memungkinkan Anda untuk menyinkronkan akses ke wilayah kode dengan mengambil dan melepaskan kunci pada objek tertentu dengan memanggil Monitor.Entermetode , Monitor.TryEnter, dan Monitor.Exit . Kunci objek menyediakan kemampuan untuk membatasi akses ke blok kode, yang biasa disebut bagian penting. Sementara utas memiliki kunci untuk objek, tidak ada utas lain yang dapat memperoleh kunci tersebut. Anda juga dapat menggunakan Monitor kelas untuk memastikan bahwa tidak ada utas lain yang diizinkan untuk mengakses bagian kode aplikasi yang dijalankan oleh pemilik kunci, kecuali utas lain menjalankan kode menggunakan objek terkunci yang berbeda. Karena kelas Monitor memiliki afinitas utas, utas yang memperoleh kunci harus melepaskan kunci dengan memanggil metode Monitor.Exit.

Gambaran Umum

Monitor memiliki fitur-fitur berikut:

  • Ini dikaitkan dengan objek sesuai permintaan.
  • Ini tidak terbatas, yang berarti dapat dipanggil langsung dari konteks apa pun.
  • Instans MonitorMonitor kelas tidak dapat dibuat; metode kelas semuanya statis. Setiap metode diteruskan objek yang disinkronkan yang mengontrol akses ke bagian penting.

Catatan

Monitor Gunakan kelas untuk mengunci objek selain string (yaitu, jenis referensi selain String), bukan jenis nilai. Untuk detailnya, lihat kelebihan beban Enter metode dan bagian Objek kunci nanti di artikel ini.

Tabel berikut ini menjelaskan tindakan yang dapat diambil oleh utas yang mengakses objek yang disinkronkan:

Tindakan Deskripsi
Enter, TryEnter Memperoleh kunci untuk objek. Tindakan ini juga menandai awal bagian penting. Tidak ada utas lain yang dapat memasuki bagian penting kecuali jika menjalankan instruksi di bagian penting menggunakan objek terkunci yang berbeda.
Wait Melepaskan kunci pada objek untuk mengizinkan utas lain mengunci dan mengakses objek. Utas panggilan menunggu sementara utas lain mengakses objek. Sinyal pulse digunakan untuk memberi tahu utas tunggu tentang perubahan pada status objek.
Pulse (sinyal), PulseAll Mengirim sinyal ke satu atau beberapa alur tunggu. Sinyal memberi tahu alur tunggu bahwa status objek terkunci telah berubah, dan pemilik kunci siap untuk melepaskan kunci. Utas tunggu ditempatkan dalam antrean siap objek sehingga pada akhirnya mungkin menerima kunci untuk objek. Setelah utas memiliki kunci, alur dapat memeriksa status baru objek untuk melihat apakah status yang diperlukan telah tercapai.
Exit Melepaskan kunci pada objek. Tindakan ini juga menandai akhir bagian penting yang dilindungi oleh objek terkunci.

Ada dua set kelebihan beban untuk Enter metode dan TryEnter . Satu set kelebihan beban memiliki ref parameter (dalam C#) atau ByRef (dalam Visual Basic) Boolean yang secara atomis diatur ke true jika kunci diperoleh, bahkan jika pengecualian dilemparkan saat memperoleh kunci. Gunakan kelebihan beban ini jika penting untuk melepaskan kunci dalam semua kasus, bahkan ketika sumber daya yang dilindungi kunci mungkin tidak dalam keadaan konsisten.

Objek kunci

Kelas Monitor terdiri dari static metode (Shared dalam Visual Basic) yang beroperasi pada objek yang mengontrol akses ke bagian penting. Informasi berikut dipertahankan untuk setiap objek yang disinkronkan:

  • Referensi ke utas yang saat ini memegang kunci.
  • Referensi ke antrean siap, yang berisi utas yang siap untuk mendapatkan kunci.
  • Referensi ke antrean tunggu, yang berisi utas yang menunggu pemberitahuan perubahan status objek terkunci.

Monitor mengunci objek (yaitu, jenis referensi), bukan jenis nilai. Meskipun Anda dapat meneruskan jenis nilai ke Enter dan Exit, itu dikotak secara terpisah untuk setiap panggilan. Karena setiap panggilan membuat objek terpisah, Enter tidak pernah memblokir, dan kode yang seharusnya dilindungi tidak benar-benar disinkronkan. Selain itu, objek yang diteruskan berbeda Exit dari objek yang diteruskan ke Enter, jadi Monitor melemparkan SynchronizationLockException pengecualian dengan pesan "Metode sinkronisasi objek dipanggil dari blok kode yang tidak disinkronkan."

Contoh berikut mengilustrasikan masalah ini. Ini meluncurkan sepuluh tugas, yang masing-masing hanya tidur selama 250 milidetik. Setiap tugas kemudian memperbarui variabel penghitung, nTasks, yang dimaksudkan untuk menghitung jumlah tugas yang benar-benar diluncurkan dan dijalankan. Karena nTasks merupakan variabel global yang dapat diperbarui oleh beberapa tugas secara bersamaan, monitor digunakan untuk melindunginya dari modifikasi simultan oleh beberapa tugas. Namun, seperti yang ditunjukkan oleh output dari contoh, setiap tugas melemparkan SynchronizationLockException pengecualian.

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.

Setiap tugas melemparkan SynchronizationLockException pengecualian karena variabel dikotak nTasks sebelum panggilan ke Monitor.Enter metode di setiap tugas. Dengan kata lain, setiap panggilan metode diteruskan variabel terpisah yang independen dari yang lain. nTasks dikotak lagi dalam panggilan ke Monitor.Exit metode . Sekali lagi, ini membuat sepuluh variabel kotak baru, yang independen satu sama lain, nTasks, dan sepuluh variabel kotak yang dibuat dalam panggilan ke Monitor.Enter metode . Pengecualian dilemparkan, maka, karena kode kami mencoba melepaskan kunci pada variabel yang baru dibuat yang sebelumnya tidak dikunci.

Meskipun Anda dapat mengetikkan variabel jenis nilai sebelum memanggil Enter dan Exit, seperti yang ditunjukkan dalam contoh berikut, dan meneruskan objek kotak yang sama ke kedua metode, tidak ada keuntungan untuk melakukan ini. Perubahan pada variabel yang tidak dikotak tidak tercermin dalam salinan berkotak, dan tidak ada cara untuk mengubah nilai salinan berkotak.

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.

Saat memilih objek yang akan disinkronkan, Anda harus mengunci hanya pada objek privat atau internal. Mengunci objek eksternal dapat mengakibatkan kebuntuan, karena kode yang tidak terkait dapat memilih objek yang sama untuk dikunci untuk tujuan yang berbeda.

Perhatikan bahwa Anda dapat menyinkronkan pada objek di beberapa domain aplikasi jika objek yang digunakan untuk kunci berasal dari MarshalByRefObject.

Bagian penting

Enter Gunakan metode dan Exit untuk menandai awal dan akhir bagian penting.

Catatan

Fungsionalitas yang disediakan oleh metode dan Exit identik dengan yang disediakan oleh pernyataan kunci di C# dan pernyataan SyncLock di Visual Basic, kecuali bahwa konstruksi bahasa membungkus Monitor.Enter(Object, Boolean) metode kelebihan beban dan Monitor.Exit metode dalam try...Enterfinally blokir untuk memastikan bahwa monitor dirilis.

Jika bagian kritis adalah serangkaian instruksi yang berdekatan, maka kunci yang diperoleh oleh Enter metode menjamin bahwa hanya satu utas yang dapat menjalankan kode tertutup dengan objek terkunci. Dalam hal ini, kami sarankan Anda menempatkan kode tersebut di try blok dan melakukan panggilan ke Exit metode dalam finally blok. Ini memastikan bahwa kunci dilepaskan bahkan jika terjadi pengecualian. Fragmen kode berikut mengilustrasikan pola ini.

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

Fasilitas ini biasanya digunakan untuk menyinkronkan akses ke metode statis atau instans kelas.

Jika bagian penting mencakup seluruh metode, fasilitas penguncian System.Runtime.CompilerServices.MethodImplAttributedapat dicapai dengan menempatkan System.Runtime.CompilerServices.MethodImplAttribute pada metode , dan menentukan Synchronized nilai dalam konstruktor . Saat Anda menggunakan atribut ini, Enter panggilan metode dan Exit tidak diperlukan. Fragmen kode berikut mengilustrasikan pola ini:

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

Perhatikan bahwa atribut menyebabkan utas saat ini menahan kunci hingga metode kembali; jika kunci dapat dirilis lebih cepat, gunakan Monitor kelas, pernyataan kunci C#, atau pernyataan Visual Basic SyncLock di dalam metode alih-alih atribut .

Meskipun dimungkinkan Enter untuk pernyataan dan Exit yang mengunci dan melepaskan objek tertentu untuk melewati batas anggota atau kelas atau keduanya, praktik ini tidak disarankan.

Pulse, PulseAll, dan Wait

Setelah utas memiliki kunci dan telah memasuki bagian penting yang dilindungi kunci, utas dapat memanggil Monitor.Waitmetode , Monitor.Pulse, dan Monitor.PulseAll .

Saat utas yang menahan kunci memanggil , kunci dilepaskan dan utas ditambahkan ke antrean tunggu objek yang disinkronkan Wait. Utas pertama dalam antrean siap, jika ada, memperoleh kunci dan memasuki bagian penting. Utas yang dipanggil Wait dipindahkan dari antrean tunggu ke antrean siap ketika Monitor.Pulse metode atau Monitor.PulseAll dipanggil oleh utas yang menahan kunci (untuk dipindahkan, utas harus berada di kepala antrean tunggu). Metode Wait ini kembali ketika utas panggilan memperoleh kembali kunci.

Ketika utas yang menahan kunci memanggil , utas Pulsedi kepala antrean tunggu dipindahkan ke antrean siap. Panggilan ke PulseAll metode memindahkan semua utas dari antrean tunggu ke antrean siap.

Memantau dan menunggu handel

Penting untuk dicatat perbedaan antara penggunaan Monitor kelas dan WaitHandle objek.

  • Kelas Monitor ini dikelola secara murni, sepenuhnya portabel, dan mungkin lebih efisien dalam hal persyaratan sumber daya sistem operasi.
  • WaitHandle Objek mewakili objek yang dapat ditunggu sistem operasi, berguna untuk menyinkronkan antara kode terkelola dan tidak terkelola, dan mengekspos beberapa fitur sistem operasi canggih seperti kemampuan untuk menunggu pada banyak objek sekaligus.

Contoh

Contoh berikut menggunakan Monitor kelas untuk menyinkronkan akses ke satu instans generator angka acak yang diwakili oleh Random kelas . Contoh membuat sepuluh tugas, yang masing-masing dijalankan secara asinkron pada utas kumpulan utas. Setiap tugas menghasilkan 10.000 angka acak, menghitung rata-ratanya, dan memperbarui dua variabel tingkat prosedur yang mempertahankan total jumlah angka acak yang berjalan yang dihasilkan dan jumlahnya. Setelah semua tugas dijalankan, kedua nilai ini kemudian digunakan untuk menghitung rata-rata keseluruhan.

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)

Karena dapat diakses dari tugas apa pun yang berjalan pada utas kumpulan utas, akses ke variabel dan n juga harus disinkronkantotal. Metode Interlocked.Add ini digunakan untuk tujuan ini.

Contoh berikut menunjukkan penggunaan Monitor gabungan kelas (diimplementasikan dengan lock konstruksi bahasa atau SyncLock ), Interlocked kelas, dan AutoResetEvent kelas . Ini mendefinisikan dua internal kelas SyncResource (dalam C#) atau Friend (di Visual Basic), dan UnSyncResource, yang masing-masing menyediakan akses yang disinkronkan dan tidak disinkronkan ke sumber daya. Untuk memastikan bahwa contoh mengilustrasikan perbedaan antara akses yang disinkronkan dan tidak disinkronkan (yang dapat terjadi jika setiap panggilan metode selesai dengan cepat), metode ini mencakup penundaan acak: untuk utas yang propertinya Thread.ManagedThreadId bahkan, metode memanggil Thread.Sleep untuk memperkenalkan penundaan 2.000 milidetik. Perhatikan bahwa, karena SyncResource kelas tidak publik, tidak ada kode klien yang mengambil kunci pada sumber daya yang disinkronkan; kelas internal itu sendiri mengambil kunci. Ini mencegah kode berbahaya mengambil kunci pada objek publik.

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.

Contoh mendefinisikan variabel, numOps, yang menentukan jumlah utas yang akan mencoba mengakses sumber daya. Utas aplikasi memanggil metode untuk akses yang disinkronkan ThreadPool.QueueUserWorkItem(WaitCallback) dan tidak disinkronkan masing-masing lima kali. Metode ThreadPool.QueueUserWorkItem(WaitCallback) ini memiliki satu parameter, delegasi yang tidak menerima parameter dan tidak mengembalikan nilai. Untuk akses yang disinkronkan SyncUpdateResource , ia memanggil metode; untuk akses yang tidak disinkronkan, ia memanggil UnSyncUpdateResource metode . Setelah setiap set panggilan metode, utas aplikasi memanggil metode AutoResetEvent.WaitOne sehingga memblokir hingga AutoResetEvent instans disinyalir.

Setiap panggilan ke SyncUpdateResource metode memanggil metode internal SyncResource.Access dan kemudian memanggil Interlocked.Decrement metode untuk mengurangi numOps penghitung. Metode Interlocked.Decrement Ini digunakan untuk mengurangi penghitung, karena jika tidak, Anda tidak dapat yakin bahwa utas kedua akan mengakses nilai sebelum nilai dekrementasi utas pertama disimpan dalam variabel. Ketika utas pekerja terakhir yang disinkronkan mengurangi penghitung menjadi nol, menunjukkan bahwa semua utas yang disinkronkan telah selesai mengakses sumber daya, SyncUpdateResource metode memanggil EventWaitHandle.Set metode , yang menandakan utas utama untuk melanjutkan eksekusi.

Setiap panggilan ke UnSyncUpdateResource metode memanggil metode internal UnSyncResource.Access dan kemudian memanggil Interlocked.Decrement metode untuk mengurangi numOps penghitung. Sekali lagi, Interlocked.Decrement metode Ini digunakan untuk mengurangi penghitung untuk memastikan bahwa utas kedua tidak mengakses nilai sebelum nilai dekrementasi utas pertama telah ditetapkan ke variabel. Ketika utas pekerja terakhir yang tidak disinkronkan menurunkan penghitung menjadi nol, menunjukkan bahwa tidak ada lagi utas yang tidak disinkronkan perlu mengakses sumber daya, UnSyncUpdateResource metode memanggil metode , yang menandakan EventWaitHandle.Set utas utama untuk melanjutkan eksekusi.

Seperti yang ditunjukkan oleh output dari contoh, akses yang disinkronkan memastikan bahwa utas panggilan keluar dari sumber daya yang dilindungi sebelum utas lain dapat mengaksesnya; setiap utas menunggu pada pendahulunya. Di sisi lain, tanpa kunci, UnSyncResource.Access metode dipanggil dalam urutan di mana utas mencapainya.