Monitor Klass
Definition
Viktigt
En del information gäller för förhandsversionen av en produkt och kan komma att ändras avsevärt innan produkten blir allmänt tillgänglig. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, avseende informationen som visas här.
Tillhandahåller en mekanism som synkroniserar åtkomsten till objekt.
public ref class Monitor abstract sealed
public ref class Monitor sealed
public static class Monitor
public sealed class Monitor
[System.Runtime.InteropServices.ComVisible(true)]
public static class Monitor
type Monitor = class
[<System.Runtime.InteropServices.ComVisible(true)>]
type Monitor = class
Public Class Monitor
Public NotInheritable Class Monitor
- Arv
-
Monitor
- Attribut
Exempel
I följande exempel används Monitor klassen för att synkronisera åtkomst till en enda instans av en slumptalsgenerator som representeras av Random klassen. I exemplet skapas tio uppgifter som var och en körs asynkront på en trådpoolstråd. Varje aktivitet genererar 10 000 slumpmässiga tal, beräknar deras genomsnitt och uppdaterar två variabler på procedurnivå som upprätthåller en löpande summa av antalet slumpmässiga tal som genereras och deras summa. När alla aktiviteter har utförts används dessa två värden för att beräkna det totala medelvärdet.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
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)
Eftersom de kan nås från alla aktiviteter som körs i en trådpoolstråd, åtkomst till variablerna total och n måste också synkroniseras. Metoden Interlocked.Add används för detta ändamål.
I följande exempel visas den kombinerade användningen av Monitor klassen (implementerad med konstruktionen lock eller SyncLock språket), Interlocked klassen och AutoResetEvent klassen. Den definierar två internal (i C#) eller Friend (i Visual Basic) klasser och SyncResourceUnSyncResource, som ger synkroniserad respektive osynkroniserad åtkomst till en resurs. För att säkerställa att exemplet illustrerar skillnaden mellan den synkroniserade och osynkroniserade åtkomsten (vilket kan vara fallet om varje metodanrop slutförs snabbt) innehåller metoden en slumpmässig fördröjning: för trådar vars Thread.ManagedThreadId egenskap är jämn anropar Thread.Sleep metoden en fördröjning på 2 000 millisekunder. Observera att eftersom SyncResource klassen inte är offentlig, tar ingen av klientkoden ett lås på den synkroniserade resursen. Själva den interna klassen tar låset. Detta förhindrar att skadlig kod låser ett offentligt objekt.
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.
I exemplet definieras en variabel, numOps, som definierar antalet trådar som ska försöka komma åt resursen. Programtråden ThreadPool.QueueUserWorkItem(WaitCallback) anropar metoden för synkroniserad och osynkroniserad åtkomst fem gånger vardera. Metoden ThreadPool.QueueUserWorkItem(WaitCallback) har en enda parameter, ett ombud som inte accepterar några parametrar och som inte returnerar något värde. För synkroniserad åtkomst anropas SyncUpdateResource metoden. För osynkroniserad åtkomst anropas UnSyncUpdateResource metoden. Efter varje uppsättning metodanrop anropar programtråden metoden AutoResetEvent.WaitOne så att den blockerar tills instansen AutoResetEvent signaleras.
Varje anrop till SyncUpdateResource-metoden anropar den interna SyncResource.Access-metoden och därefter Interlocked.Decrement-metoden för att minska numOps-räknaren. Metoden Interlocked.Decrement används för att minska räknaren, eftersom du annars inte kan vara säker på att en andra tråd kommer åt värdet innan en första tråds dementerade värde har lagrats i variabeln. När den sista synkroniserade arbetstråden minskar räknaren till noll, vilket indikerar att alla synkroniserade trådar har slutfört åtkomsten till resursen, anropar SyncUpdateResource metoden EventWaitHandle.Set metoden, som signalerar huvudtråden så att den kan fortsätta att köra.
Varje anrop till UnSyncUpdateResource-metoden anropar den interna UnSyncResource.Access-metoden och därefter Interlocked.Decrement-metoden för att minska numOps-räknaren.
Interlocked.Decrement Återigen används metoden för att minska räknaren för att säkerställa att en andra tråd inte har åtkomst till värdet innan en första tråds avstängda värde har tilldelats variabeln. När den sista osynkroniserade arbetstråden minskar räknaren till noll, vilket indikerar att inga fler osynkroniserade trådar behöver komma åt resursen, anropar UnSyncUpdateResource-metoden EventWaitHandle.Set-metoden, som signalerar huvudtråden att fortsätta exekveringen.
Som utdata från exemplet visar ser synkroniserad åtkomst till att den anropande tråden avslutar den skyddade resursen innan en annan tråd kan komma åt den. varje tråd väntar på sin föregångare. Å andra sidan anropas metoden utan låset UnSyncResource.Access i den ordning trådarna når den.
Kommentarer
Med Monitor klassen kan du synkronisera åtkomst till en kodregion genom att ta och frigöra ett lås på ett visst objekt genom att anropa Monitor.Entermetoderna , Monitor.TryEnteroch Monitor.Exit . Objektlås ger möjlighet att begränsa åtkomsten till ett kodblock, som ofta kallas för ett kritiskt avsnitt. En tråd äger låset för ett objekt, men ingen annan tråd kan hämta låset. Du kan också använda Monitor klassen för att se till att ingen annan tråd får åtkomst till ett avsnitt av programkoden som körs av låsägaren, såvida inte den andra tråden kör koden med ett annat låst objekt. Eftersom klassen Monitor har trådtillhörighet måste tråden som förvärvade ett lås frigöra det genom att anropa metoden Monitor.Exit.
Översikt
Monitor har följande funktioner:
- Det är associerat med ett objekt på begäran.
- Det är obundet, vilket innebär att det kan anropas direkt från valfri kontext.
- Det går inte att skapa en instans av Monitor-klassen. Metoderna i Monitor-klassen är alla statiska. Varje metod skickas det synkroniserade objektet som styr åtkomsten till det kritiska avsnittet.
Note
Monitor Använd klassen för att låsa andra objekt än strängar (dvs. andra referenstyper än String), inte värdetyper. Mer information finns i överlagringarna av Enter metoden och avsnittet Lås objekt senare i den här artikeln.
I följande tabell beskrivs de åtgärder som kan vidtas av trådar som har åtkomst till synkroniserade objekt:
| Åtgärd | Description |
|---|---|
| Enter, TryEnter | Förvärvar ett lås för ett objekt. Den här åtgärden markerar också början på ett kritiskt avsnitt. Ingen annan tråd kan ange det kritiska avsnittet om den inte kör anvisningarna i det kritiska avsnittet med hjälp av ett annat låst objekt. |
| Wait | Frigör låset på ett objekt för att tillåta andra trådar att låsa och komma åt objektet. Den anropande tråden väntar medan en annan tråd kommer åt objektet. Pulssignaler används för att meddela väntande trådar om ändringar i ett objekts tillstånd. |
| Pulse (signal), PulseAll | Skickar en signal till en eller flera väntande trådar. Signalen meddelar en väntande tråd att tillståndet för det låsta objektet har ändrats och låsets ägare är redo att släppa låset. Den väntande tråden placeras i objektets redo kö så att den så småningom kan ta emot låset för objektet. När tråden har låset kan den kontrollera objektets nya tillstånd för att se om det nödvändiga tillståndet har nåtts. |
| Exit | Frigör låset på ett objekt. Den här åtgärden markerar också slutet på ett kritiskt avsnitt som skyddas av det låsta objektet. |
Det finns två uppsättningar av överbelastningar för metoderna Enter och TryEnter. En uppsättning överlagringar har en ref (i C#) eller ByRef (i Visual Basic) Boolean parameter som är atomiskt inställd på true om låset hämtas, även om ett undantag utlöses när låset hämtas. Använd dessa överlagringar om det är avgörande att frigöra låset i alla fall, även om resurserna som låset skyddar kanske inte är i ett konsistent tillstånd.
Låsobjektet
Klassen Monitor består av static (Shared i Visual Basic) metoder som fungerar på ett objekt som styr åtkomsten till det kritiska avsnittet. Följande information bevaras för varje synkroniserat objekt:
- En referens till tråden som för närvarande innehåller låset.
- En referens till en färdig kö som innehåller trådarna som är redo att hämta låset.
- En referens till en väntekö som innehåller trådarna som väntar på meddelande om en ändring i tillståndet för det låsta objektet.
Monitor låser objekt (dvs. referenstyper), inte värdetyper. Du kan skicka en värdetyp till Enter och Exit, men den boxas separat för varje anrop. Eftersom varje anrop skapar ett separat objekt, Enter blockeras aldrig, och koden som det förmodligen skyddar är inte riktigt synkroniserad. Dessutom skiljer sig objektet som skickas till Exit från objektet som skickades till Enter, så Monitor genererar SynchronizationLockException undantag med meddelandet "Objektsynkroniseringsmetod anropades från ett osynkroniserat kodblock".
I följande exempel visas det här problemet. Den startar tio uppgifter, som var och en bara sover i 250 millisekunder. Varje uppgift uppdaterar sedan en räknarvariabel, nTasks, som är avsedd att räkna antalet aktiviteter som faktiskt startades och kördes. Eftersom nTasks är en global variabel som kan uppdateras av flera uppgifter samtidigt, används en övervakare för att skydda den från samtidig ändring av flera uppgifter. Men som utdata från exemplet visar utlöser var och en av uppgifterna ett SynchronizationLockException undantag.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
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.
Varje aktivitet utlöser ett SynchronizationLockException undantag eftersom variabeln nTasks boxas före anropet Monitor.Enter till metoden i varje aktivitet. Med andra ord skickas varje metodanrop en separat variabel som är oberoende av de andra.
nTasks boxas igen vid anropet av metoden Monitor.Exit. Återigen skapar detta tio nya boxade variabler, som är oberoende av varandra, nTasksoch de tio boxade variablerna som skapades i anropet Monitor.Enter till metoden. Undantaget genereras därför eftersom vår kod försöker frigöra ett lås på en nyligen skapad variabel som inte har låsts tidigare.
Även om du kan ange en värdetypsvariabel innan du anropar Enter och Exit, som du ser i följande exempel, och skicka samma rutade objekt till båda metoderna, finns det ingen fördel med att göra detta. Ändringar i den oboxade variabeln återspeglas inte i den boxade kopian och det finns inget sätt att ändra värdet för den boxade kopian.
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.
När du väljer ett objekt som ska synkroniseras på bör du endast låsa på privata eller interna objekt. Låsning på externa objekt kan leda till dödlägen eftersom orelaterad kod kan välja samma objekt att låsa på i olika syften.
Observera att du kan synkronisera på ett objekt i flera programdomäner om objektet som används för låset härleds från MarshalByRefObject.
Det kritiska avsnittet
Enter Använd metoderna och Exit för att markera början och slutet av ett kritiskt avsnitt.
Note
Funktionerna som tillhandahålls av Enter metoderna och Exit är identiska med de som tillhandahålls av låssatsen i C# och SyncLock-instruktionen i Visual Basic, förutom att språket konstruerar omsluter Monitor.Enter(Object, Boolean) metodens överbelastning och Monitor.Exit metoden i en try...
finally block för att säkerställa att monitorn frigörs.
Om det kritiska avsnittet är en uppsättning sammanhängande instruktioner garanterar låset som hämtas av Enter metoden att endast en enda tråd kan köra den omslutna koden med det låsta objektet. I det här fallet rekommenderar vi att du placerar koden i ett try block och placerar anropet Exit till metoden i ett finally block. Detta säkerställer att låset släpps även om ett undantag inträffar. Följande kodfragment illustrerar det här mönstret.
// 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
Den här funktionen används vanligtvis för att synkronisera åtkomst till en statisk metod eller instansmetod för en klass.
Om ett kritiskt avsnitt sträcker sig över en hel metod kan låsningsmekanismen uppnås genom att placera System.Runtime.CompilerServices.MethodImplAttribute på metoden och ange Synchronized värdet i konstruktorn för System.Runtime.CompilerServices.MethodImplAttribute. När du använder det här attributet behövs inte metodanropen Enter och Exit . Följande kodfragment illustrerar det här mönstret:
[MethodImplAttribute(MethodImplOptions.Synchronized)]
void MethodToLock()
{
// Method implementation.
}
<MethodImplAttribute(MethodImplOptions.Synchronized)>
Sub MethodToLock()
' Method implementation.
End Sub
Observera att attributet gör att den aktuella tråden håller låset tills metoden returneras. Om låset kan släppas tidigare använder du Monitor -klassen, C#- låssatsen eller Visual Basic SyncLock-instruktionen i metoden i stället för attributet.
Även om det är möjligt för instruktionerna Enter och Exit som låser och släpper ett visst objekt att korsa medlems- eller klassgränser, eller både och, rekommenderas inte den här metoden.
Pulse, PulseAll och Wait
När en tråd äger låset och har angett det kritiska avsnittet som låset skyddar kan den anropa Monitor.Waitmetoderna , Monitor.Pulseoch Monitor.PulseAll .
När tråden som innehåller låsanropen Waitfrigörs låset och tråden läggs till i den väntande kön för det synkroniserade objektet. Den första tråden i den körklara kön, om någon, får låset och går in i det kritiska avsnittet. Tråden som anropade Wait flyttas från väntkön till beredskapskön när antingen Monitor.Pulse eller Monitor.PulseAll -metoden anropas av tråden som håller låset (för att flyttas måste tråden vara i början av väntkön). Metoden Wait returnerar när den anropande tråden tar tillbaka låset.
När tråden som håller låset anropar Pulse, flyttas tråden vid toppen av väntekön till beredskapskön. Anropet PulseAll till metoden flyttar alla trådar från den väntande kön till den färdiga kön.
Övervakare och väntehandtag
Det är viktigt att notera skillnaden mellan användningen av Monitor klassen och WaitHandle objekten.
- Klassen Monitor är helt hanterad, helt portabel och kan vara effektivare när det gäller resurskrav för operativsystem.
- WaitHandle objekt representerar väntebara objekt i operativsystemet, är användbara för synkronisering mellan hanterad och ohanterad kod och exponerar vissa avancerade operativsystemfunktioner som möjligheten att vänta på många objekt samtidigt.
Egenskaper
| Name | Description |
|---|---|
| LockContentionCount |
Hämtar antalet gånger det uppstod konkurrens när du försökte ta övervakarens lås. |
Metoder
| Name | Description |
|---|---|
| Enter(Object, Boolean) |
Hämtar ett exklusivt lås på det angivna objektet och anger atomiskt ett värde som anger om låset har tagits. |
| Enter(Object) |
Hämtar ett exklusivt lås på det angivna objektet. |
| Exit(Object) |
Frigör ett exklusivt lås på det angivna objektet. |
| IsEntered(Object) |
Avgör om den aktuella tråden innehåller låset på det angivna objektet. |
| Pulse(Object) |
Meddelar en tråd i väntekön om en ändring i det låsta objektets tillstånd. |
| PulseAll(Object) |
Meddelar alla väntande trådar om en ändring i objektets tillstånd. |
| TryEnter(Object, Boolean) |
Försöker hämta ett exklusivt lås på det angivna objektet och anger atomiskt ett värde som anger om låset har tagits. |
| TryEnter(Object, Int32, Boolean) |
Försök, för det angivna antalet millisekunder, att hämta ett exklusivt lås på det angivna objektet och atomiskt anger ett värde som anger om låset har tagits. |
| TryEnter(Object, Int32) |
Försök, för det angivna antalet millisekunder, att hämta ett exklusivt lås på det angivna objektet. |
| TryEnter(Object, TimeSpan, Boolean) |
Försöker under den angivna tiden att skaffa ett exklusivt lås på det angivna objektet och anger atomiskt ett värde som anger om låset har tagits. |
| TryEnter(Object, TimeSpan) |
Försöker under den angivna tidsperioden att hämta ett exklusivt lås på det angivna objektet. |
| TryEnter(Object) |
Försöker hämta ett exklusivt lås på det angivna objektet. |
| Wait(Object, Int32, Boolean) |
Frigör låset på ett objekt och blockerar den aktuella tråden tills den tar igen låset. Om det angivna tidsgränsintervallet förflutit anger tråden den färdiga kön. Den här metoden anger också om synkroniseringsdomänen för kontexten (om i en synkroniserad kontext) avslutas innan väntan och återkrävs efteråt. |
| Wait(Object, Int32) |
Frigör låset på ett objekt och blockerar den aktuella tråden tills den tar igen låset. Om det angivna tidsgränsintervallet förflutit anger tråden den färdiga kön. |
| Wait(Object, TimeSpan, Boolean) |
Frigör låset på ett objekt och blockerar den aktuella tråden tills den tar igen låset. Om det angivna tidsgränsintervallet förflutit anger tråden den färdiga kön. Du kan också avsluta synkroniseringsdomänen för den synkroniserade kontexten innan du väntar och hämtar domänen igen efteråt. |
| Wait(Object, TimeSpan) |
Frigör låset på ett objekt och blockerar den aktuella tråden tills den tar igen låset. Om det angivna tidsgränsintervallet förflutit anger tråden den färdiga kön. |
| Wait(Object) |
Frigör låset på ett objekt och blockerar den aktuella tråden tills den tar igen låset. |
Gäller för
Trådsäkerhet
Den här typen är trådsäker.