System.Threading.Monitor sınıfı
Bu makale, bu API'nin başvuru belgelerine ek açıklamalar sağlar.
Monitor sınıfı, , Monitor.TryEnterve Monitor.Exit yöntemlerini çağırarak belirli bir nesne üzerinde kilit alıp serbest bırakarak kodun Monitor.Enterbir bölgesine erişimi eşitlemenize olanak tanır. Nesne kilitleri, genellikle kritik bölüm olarak adlandırılan bir kod bloğuna erişimi kısıtlama olanağı sağlar. Bir iş parçacığı bir nesnenin kilidine sahip olsa da, başka hiçbir iş parçacığı bu kilidi edinemez. Sınıfını Monitor , diğer iş parçacığı kodu farklı bir kilitli nesne kullanarak yürütmediği sürece kilit sahibi tarafından yürütülen uygulama kodunun bir bölümüne başka bir iş parçacığının erişmesine izin verilmediğinden emin olmak için de kullanabilirsiniz. İzleyici sınıfı iş parçacığı benzitesine sahip olduğundan, kilit alan iş parçacığının Monitor.Exit yöntemini çağırarak kilidi serbest bırakması gerekir.
Genel bakış
Monitor aşağıdaki özelliklere sahiptir:
- İsteğe bağlı bir nesneyle ilişkilendirilir.
- İlişkisizdir, yani doğrudan herhangi bir bağlamdan çağrılabilir.
- Sınıfın Monitor bir örneği oluşturulamaz; sınıfın yöntemlerinin Monitor tümü statiktir. Her yöntem, kritik bölüme erişimi denetleyen eşitlenmiş nesneye geçirilir.
Not
Monitor dizeleri (başka bir deyişle, dışındaki başvuru türleri), değer türleri Stringdışındaki nesneleri kilitlemek için sınıfını kullanın. Ayrıntılar için bu makalenin devamında yönteminin Enter aşırı yüklemeleri ve Nesne kilitleme bölümüne bakın.
Aşağıdaki tabloda, eşitlenmiş nesnelere erişen iş parçacıkları tarafından gerçekleştirilebilecek eylemler açıklanmaktadır:
Eylem | Açıklama |
---|---|
Enter, TryEnter | Bir nesne için kilit alır. Bu eylem, kritik bir bölümün başlangıcını da işaretler. Kritik bölümdeki yönergeleri farklı bir kilitli nesne kullanarak yürütmediği sürece başka hiçbir iş parçacığı kritik bölüme giremez. |
Wait | Diğer iş parçacıklarının nesneyi kilitlemesine ve nesneye erişmesine izin vermek için nesne üzerindeki kilidi serbest bırakır. Başka bir iş parçacığı nesneye erişirken çağıran iş parçacığı bekler. Darbe sinyalleri, bekleyen iş parçacıklarını nesnenin durumundaki değişiklikler hakkında bilgilendirmek için kullanılır. |
Pulse (sinyal), PulseAll | Bir veya daha fazla bekleyen iş parçacığına sinyal gönderir. Sinyal, bekleyen bir iş parçacığına kilitli nesnenin durumunun değiştiğini ve kilidin sahibinin kilidi serbest bırakmaya hazır olduğunu bildirir. Bekleyen iş parçacığı nesnenin hazır kuyruğuna yerleştirilir, böylece nesnenin kilidini alabilir. İş parçacığı kilidine sahip olduktan sonra, gerekli duruma ulaşılıp ulaşılamadığını görmek için nesnenin yeni durumunu denetleyebilir. |
Exit | Bir nesne üzerindeki kilidi serbest bırakır. Bu eylem, kilitli nesne tarafından korunan kritik bir bölümün sonunu da işaretler. |
ve TryEnter yöntemleri için Enter iki aşırı yükleme kümesi vardır. Bir aşırı yükleme kümesi, kilit alınırken bir özel durum oluştuğunda bile atomik olarak ayarlanmış bir ref
(C#'de) veya ByRef
(Visual Basic'te) Boolean parametresine true
sahiptir. Kilidin koruduğu kaynaklar tutarlı bir durumda olmasa bile kilidin her durumda serbest bırakılması kritikse bu aşırı yüklemeleri kullanın.
Lock nesnesi
İzleyici sınıfı, kritik bölüme erişimi denetleyen bir nesne üzerinde çalışan (Shared
Visual Basic'te) yöntemlerden oluşur static
. Eşitlenen her nesne için aşağıdaki bilgiler korunur:
- Şu anda kilidi tutan iş parçacığına başvuru.
- Kilidi almaya hazır iş parçacıklarını içeren hazır kuyruğa başvuru.
- Kilitli nesnenin durumundaki bir değişikliğin bildirimini bekleyen iş parçacıklarını içeren bir bekleme kuyruğu başvurusu.
Monitor nesneleri (başvuru türleri) kilitler, değer türlerini kilitlemez. ve Exitöğesine Enter bir değer türü geçirebilirsiniz ancak her çağrı için ayrı olarak kutulanır. Her çağrı ayrı bir nesne oluşturduğundan, Enter hiçbir zaman engellemez ve koruduğu varsayılan kod gerçekten eşitlenmez. Buna ek olarak, geçirilen Exit nesne, geçirilen Enternesneden farklıdır, bu nedenle Monitor "Nesne eşitleme yöntemi zaman uyumsuz bir kod bloğundan çağrıldı" iletisiyle özel durum oluşturur SynchronizationLockException .
Aşağıdaki örnekte bu sorun gösterilmektedir. Her biri 250 milisaniye boyunca uyuyan on görev başlatır. Ardından her görev, nTasks
gerçekten başlatılan ve yürütülen görev sayısını saymaya yönelik bir sayaç değişkenini güncelleştirir. nTasks
Birden çok görev tarafından aynı anda güncelleştirilebilen genel bir değişken olduğundan, birden çok görev tarafından aynı anda değiştirilmesini korumak için bir izleyici kullanılır. Ancak, örnekteki çıktıda gösterildiği gibi, görevlerin her biri bir SynchronizationLockException özel durum oluşturur.
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.
Değişken, her görevdeki nTasks
yönteme yapılan çağrıdan önce kutulandığından Monitor.Enter her görev bir SynchronizationLockException özel durum oluşturur. Başka bir deyişle, her yöntem çağrısı diğerlerinden bağımsız ayrı bir değişken geçirilir. nTasks
yöntemine yapılan çağrıda Monitor.Exit yeniden kutulanır. Bir kez daha bu, birbirinden bağımsız nTasks
on yeni kutulanmış değişken ve yöntemi çağrısında Monitor.Enter oluşturulan on kutulanmış değişken oluşturur. Özel durum oluşturulur, çünkü kodumuz daha önce kilitlenmemiş yeni oluşturulan bir değişkende kilit bırakmaya çalışır.
Aşağıdaki örnekte gösterildiği gibi ve Exitçağırmadan Enter önce bir değer türü değişkeni kutulayabilir ve aynı kutulanmış nesneyi her iki yönteme de geçirebilirsiniz, ancak bunu yapmanın bir avantajı yoktur. Kutulanmamış değişkendeki değişiklikler kutulanmış kopyaya yansıtılmaz ve kutulanmış kopyanın değerini değiştirmenin hiçbir yolu yoktur.
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.
Eşitlenecek nesneyi seçerken, yalnızca özel veya iç nesnelere kilitlemelisiniz. İlişkili olmayan kod farklı amaçlarla kilitlenmek üzere aynı nesneleri seçebileceğinden, dış nesnelere kilitlenmeye neden olabilir.
Kilit için kullanılan nesne öğesinden MarshalByRefObjecttüretilirse, birden çok uygulama etki alanında bulunan bir nesne üzerinde eşitleme yapabileceğinizi unutmayın.
Kritik bölüm
Kritik bir bölümün başlangıcını Enter ve sonunu işaretlemek için ve Exit yöntemlerini kullanın.
Not
ve yöntemleri tarafından Enter sağlanan işlevsellik, C# içindeki kilit deyimi ve Visual Basic'teki SyncLock deyimi tarafından sağlanan işlevle aynıdır, ancak dil yapılarının yöntem aşırı yüklemesini Monitor.Enter(Object, Boolean) ve Monitor.Exit yöntemini bir try
.Exit..finally
bloğunu sunun.
Kritik bölüm bitişik yönergeler kümesiyse, yöntemi tarafından Enter alınan kilit, kapalı kodu kilitli nesneyle yalnızca tek bir iş parçacığının yürütebileceğini garanti eder. Bu durumda, bu kodu bir try
bloğa yerleştirmenizi ve yöntemine çağrıyı Exit bir finally
blokta yerleştirmenizi öneririz. Bu, bir özel durum oluştuğunda bile kilidin serbest bırakılmasını sağlar. Aşağıdaki kod parçası bu düzeni gösterir.
// 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
Bu tesis genellikle bir sınıfın statik veya örnek yöntemine erişimi eşitlemek için kullanılır.
Kritik bir bölüm yöntemin tamamına yayılmışsa, kilitleme tesisi yöntemine yerleştirilip System.Runtime.CompilerServices.MethodImplAttribute oluşturucusunda System.Runtime.CompilerServices.MethodImplAttributedeğeri belirtilerek Synchronized gerçekleştirilebilir. Bu özniteliği kullandığınızda ve EnterExit yöntemi çağrıları gerekli değildir. Aşağıdaki kod parçası bu düzeni gösterir:
[MethodImplAttribute(MethodImplOptions.Synchronized)]
void MethodToLock()
{
// Method implementation.
}
<MethodImplAttribute(MethodImplOptions.Synchronized)>
Sub MethodToLock()
' Method implementation.
End Sub
özniteliğinin geçerli iş parçacığının yöntem dönene kadar kilidi tutmasına neden olduğunu unutmayın; kilit daha önce serbest bırakılabilirse, özniteliği yerine yönteminin Monitor içindeki sınıfını, C# lock deyimini veya Visual Basic SyncLock deyimini kullanın.
Belirli bir nesneyi kilitleyip serbest bırakan ve Exit deyimlerinin üye veya sınıf sınırlarını ya da her ikisini birden aşması mümkün Enter olsa da, bu uygulama önerilmez.
Pulse, PulseAll ve Wait
Bir iş parçacığı kilidin sahibi olduktan ve kilidin koruduğu kritik bölüme girdikten sonra , Monitor.Pulseve Monitor.PulseAll yöntemlerini çağırabilirMonitor.Wait.
Kilidi tutan iş parçacığı çağırdığında Wait, kilit serbest bırakılır ve iş parçacığı eşitlenen nesnenin bekleme kuyruğuna eklenir. Hazır sıradaki ilk iş parçacığı varsa kilidi alır ve kritik bölüme girer. çağrılan Wait iş parçacığı, veya Monitor.PulseAll yöntemi kilidi tutan iş parçacığı tarafından çağrıldığında Monitor.Pulse bekleme kuyruğundan hazır kuyruğa taşınır (taşınabilmesi için, iş parçacığı bekleme kuyruğunun başında olmalıdır). çağıran Wait iş parçacığı kilidi yeniden alırsa yöntemi döndürür.
Kilidi tutan iş parçacığı çağırdığında Pulse, bekleme kuyruğunun başındaki iş parçacığı hazır kuyruğa taşınır. yöntemine yapılan PulseAll çağrı, bekleyen kuyruktan hazır kuyruğa tüm iş parçacıklarını taşır.
İzleyiciler ve bekleme tutamaçları
Sınıfın WaitHandle ve nesnelerin kullanımı Monitor arasındaki ayrımı not etmek önemlidir.
- Sınıfı Monitor tamamen yönetilir, tamamen taşınabilir ve işletim sistemi kaynak gereksinimleri açısından daha verimli olabilir.
- WaitHandle nesneler işletim sistemi tarafından beklenebilen nesneleri temsil eder, yönetilen ve yönetilmeyen kod arasında eşitleme yapmak için yararlıdır ve aynı anda birçok nesnede bekleme yeteneği gibi bazı gelişmiş işletim sistemi özelliklerini kullanıma sunar.
Örnekler
Aşağıdaki örnek, sınıfı tarafından Monitor temsil edilen rastgele bir sayı oluşturucunun tek bir örneğine erişimi eşitlemek için sınıfını Random kullanır. Örnek, her biri bir iş parçacığı havuzu iş parçacığında zaman uyumsuz olarak yürütülen on görev oluşturur. Her görev 10.000 rastgele sayı oluşturur, bunların ortalamasını hesaplar ve oluşturulan rastgele sayı sayısının ve toplamlarının çalışan toplamını koruyan iki yordam düzeyinde değişkeni güncelleştirir. Tüm görevler yürütüldükten sonra, bu iki değer genel ortalamayı hesaplamak için kullanılır.
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)
İş parçacığı havuzu iş parçacığında çalışan herhangi bir görevden erişilebildiği için değişkenlere total
erişim ve n
ayrıca eşitlenmesi gerekir. Interlocked.Add yöntemi bu amaç için kullanılır.
Aşağıdaki örnekte sınıfın (veya SyncLock
dil yapısıyla lock
uygulanır), sınıfın ve sınıfın Interlocked birleştirilmiş kullanımı Monitor gösterilmektedirAutoResetEvent. Bir kaynağa eşitlenmiş ve eşitlenmemiş erişim sağlayan iki internal
(C#içinde) veya Friend
(Visual Basic'te) ve UnSyncResource
sınıflarını SyncResource
tanımlar. Örnekte eşitlenmiş ve eşitlenmemiş erişim arasındaki farkın gösterildiğinden emin olmak için (her yöntem çağrısı hızla tamamlanırsa bu durum söz konusu olabilir), yöntem rastgele bir gecikme içerir: özelliği eşit olan Thread.ManagedThreadId iş parçacıkları için yöntem, 2.000 milisaniyelik bir gecikmeye neden olmaya çağırır Thread.Sleep . SyncResource
Sınıfı genel olmadığından, istemci kodunun hiçbiri eşitlenen kaynak üzerinde kilit almaz; iç sınıfın kendisi kilidi alır. Bu, kötü amaçlı kodun ortak bir nesneye kilitlenmesini önler.
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.
Örnek, numOps
kaynağa erişmeye çalışacak iş parçacığı sayısını tanımlayan bir değişkeni tanımlar. Uygulama iş parçacığı eşitlenmiş ve eşitlenmemiş erişim için yöntemini beş kez çağırır ThreadPool.QueueUserWorkItem(WaitCallback) . yönteminde ThreadPool.QueueUserWorkItem(WaitCallback) tek bir parametre, parametre kabul eden ve değer döndürmez bir temsilci bulunur. Eşitlenmiş erişim için yöntemini çağırır SyncUpdateResource
; eşitlenmemiş erişim için yöntemini çağırır UnSyncUpdateResource
. Her yöntem kümesi çağrılarından sonra, uygulama iş parçacığı AutoResetEvent.WaitOne yöntemini çağırarak örnek sinyal alınana kadar engellemesini AutoResetEvent sağlar.
Yöntemine yapılan SyncUpdateResource
her çağrı iç SyncResource.Access
yöntemi çağırır ve ardından sayacını azaltmaya numOps
yönelik yöntemini çağırırInterlocked.Decrement. Interlocked.Decrement yöntemi sayacın azalmasını sağlamak için kullanılır, aksi takdirde ikinci bir iş parçacığının değere ilk iş parçacığının azalan değeri değişkende depolanmadan önce erişeceğinden emin olamazsınız. Son eşitlenen çalışan iş parçacığı, tüm eşitlenen iş parçacıklarının kaynağa erişmesinin tamamlandığını belirterek sayacı sıfıra düşürdüyse, SyncUpdateResource
yöntemi yöntemini çağırır EventWaitHandle.Set ve bu yöntem ana iş parçacığının yürütmeye devam etmesi için sinyal gönderir.
Yöntemine yapılan UnSyncUpdateResource
her çağrı iç UnSyncResource.Access
yöntemi çağırır ve ardından sayacını azaltmaya numOps
yönelik yöntemini çağırırInterlocked.Decrement. Bir kez daha, ilk iş parçacığının Interlocked.Decrement azalan değeri değişkenine atanmadan önce ikinci bir iş parçacığının değere erişmediğinden emin olmak için sayacı azaltma yöntemi kullanılır. Son eşitlenmemiş çalışan iş parçacığı sayacı sıfır olarak azalttığında ve artık eşitlenmemiş iş parçacıklarının kaynağa erişmesi gerekmediğini gösterirse, UnSyncUpdateResource
yöntem yöntemini çağırır EventWaitHandle.Set ve bu da ana iş parçacığının yürütmeye devam etmesi için sinyal gönderir.
Örnekteki çıktıda gösterildiği gibi, eşitlenmiş erişim çağıran iş parçacığının başka bir iş parçacığına erişmeden önce korumalı kaynaktan çıkmasını sağlar; her iş parçacığı öncülünü bekler. Öte yandan, kilit olmadan, UnSyncResource.Access
yöntem iş parçacıklarının ulaşıldığı sırayla çağrılır.