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.
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk