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 (Shared
Visual 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, nTasks
amely 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, nTasks
valamint 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 try
Exit 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 total
n
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 UnSyncResource
szinkronizá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, numOps
amely 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.
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: