Monitors
Microsoft Silverlight will reach end of support after October 2021. Learn more.
The Monitor class enables synchronized access to regions of code. Access is gained by taking and releasing a lock on a particular object by using the Monitor.Enter, Monitor.TryEnter, and Monitor.Exit methods. Once you have the lock, you can use the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods to allow other threads to gain access to the synchronized regions. The Wait method releases the lock if it is held and waits to be notified. When Wait is notified, it obtains the lock again and returns. The Pulse and PulseAll methods signal for the next thread or threads in the wait queue to proceed.
The Visual Basic SyncLock and C# lock statements use Monitor.Enter to take the lock and Monitor.Exit to release it. The advantage of using the language statements is that everything in the lock or SyncLock block is included in a Try statement. The Try statement has a Finally block to guarantee that the lock is released.
Note: |
---|
Beginning with Silverlight 4, there are two sets of overloads for the Enter and TryEnter methods. One set of overloads has a ref (ByRef in Visual Basic) Boolean parameter that is atomically set to true if the lock is acquired, even if an exception is thrown when acquiring the lock. Use these overloads if it is critical to release the lock in all cases, even when the resources the lock is protecting might not be in a consistent state. |
Monitor locks objects (that is, reference types), not value types. Although you can pass a value type to Enter and Exit, the value is boxed separately for each call. Because each call creates a separate object, Enter never blocks, and the code it is supposedly protecting is not really synchronized. In addition, the object passed to Exit is different from the object passed to Enter, so Monitor throws SynchronizationLockException with the message "Object synchronization method was called from an unsynchronized block of code." The following example illustrates these problems.
Private x As Integer
' Each time Monitor.Enter executes, the integer value is boxed.
' That is, a new object containing the value of x is created
' each time the code is executed, so that Enter never blocks.
Monitor.Enter(x)
Try
' Code that needs to be protected by the monitor.
Finally
' Always use Finally to ensure that you exit the Monitor.
' The following line creates another object containing
' the value of x, and throws SynchronizationLockException
' because the second object is a different instance from
' the first.
Monitor.Exit(x)
End Try
private int x;
// Each time Monitor.Enter executes, the integer value is boxed.
// That is, a new object containing the value of x is created
// each time the code is executed, so that Enter never blocks.
Monitor.Enter(x);
try {
// Code that needs to be protected by the monitor.
}
finally {
// Always use Finally to ensure that you exit the Monitor.
// The following line creates another object containing
// the value of x, and throws SynchronizationLockException
// because the second object is a different instance from
// the first.
Monitor.Exit(x);
}
Although you can box a value type variable before you call Enter and Exit, as shown in the following example, and you can pass the same boxed object to both methods, there is no advantage to doing this. Changes to the variable are not reflected in the boxed copy, and there is no way to change the value of the boxed copy.
Private o As Object = x
private Object o = x;
The following example demonstrates the combined use of the Monitor class (implemented with the lock and SyncLock compiler statements), the Interlocked class, and the AutoResetEvent class. For a comparison of the different kinds of synchronization mechanisms, see Synchronization Primitives.
Imports System.Threading
' Note: The class whose internal public member is the synchronizing method
' is not public; none of the client code takes a lock on the Resource object.
' The member of the nonpublic class takes the lock on itself. Written this
' way, malicious code cannot take a lock on a public object.
Friend Class SyncResource
Public Sub Access(threadNum As Int32)
' Uses Monitor class to enforce synchronization.
SyncLock Me
' The following code makes even-numbered threads yield and
' wait, but because of the lock other threads cannot access
' the resource.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Example.Display( _
String.Format("Start synchronized resource access (Thread={0})" & vbLf, _
threadNum))
Thread.Sleep(200)
Example.Display( _
String.Format("Stop synchronized resource access (Thread={0})" & vbLf, _
threadNum))
End SyncLock
End Sub
End Class
' Without the lock, the method is called in the order in which
' threads reach it.
Friend Class UnSyncResource
Public Sub Access(threadNum As Int32)
' Does not use Monitor class to enforce synchronization.
' The following code makes even-numbered threads yield and
' wait, and the odd-numbered threads can access the resource
' ahead of them.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Example.Display( _
String.Format("Start unsynchronized resource access (Thread={0})" & vbLf, _
threadNum))
Thread.Sleep(200)
Example.Display( _
String.Format("Stop unsynchronized resource access (Thread={0})" & vbLf, _
threadNum))
End Sub
End Class
Public Class Example
Private Shared numAsyncOps As Int32 = 5
Private Shared asyncOpsAreDone As New AutoResetEvent(False)
Private Shared SyncRes As New SyncResource()
Private Shared UnSyncRes As New UnSyncResource()
Private Shared threadNum As Int32
Private Shared outputBlock As System.Windows.Controls.TextBlock
Private Shared running As Boolean = False
Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)
Example.outputBlock = outputBlock
outputBlock.Text = "Click here to run the demo." & vbLf
AddHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp
End Sub
Private Shared Sub MouseUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
If running Then
' Show that the UI is responsive.
outputBlock.Text &= "MouseUp." & vbLf
Else
outputBlock.Text = "Click here to show that the UI is responsive." & vbLf
' A separate thread must be used to run the demo, so the main
' application thread doesn't block.
Dim t As New Thread(AddressOf RunDemo)
t.Start()
running = True
End If
End Sub
Private Shared Sub RunDemo()
For threadNum = 0 To 4
ThreadPool.QueueUserWorkItem(AddressOf SyncUpdateResource, threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Display("All synchronized operations have completed." & vbLf & vbLf)
' Reset the thread count for unsynchronized calls.
numAsyncOps = 5
For threadNum = 0 To 4
ThreadPool.QueueUserWorkItem(AddressOf UnSyncUpdateResource, threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Display("All unsynchronized thread operations have completed." & vbLf & vbLf)
Display("Refresh the page to run the demo again." & vbLf)
End Sub
' The callback method's signature MUST match that of
' a System.Threading.TimerCallback delegate
' (it takes an Object parameter and returns void).
Friend Shared Sub SyncUpdateResource(state As Object)
' This calls the internal synchronized method, passing
' a thread number.
SyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, because you cannot know whether another
' thread will access the value before this thread's incremented
' value has been stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
' Announce that all thread calls are done.
asyncOpsAreDone.Set()
End If
End Sub
' The callback method's signature MUST match that of
' a System.Threading.TimerCallback delegate
' (it takes an Object parameter and returns void).
Friend Shared Sub UnSyncUpdateResource(state As [Object])
' This calls the unsynchronized method, passing
' a thread number.
UnSyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, because you cannot know whether another
' thread will access the value before this thread's incremented
' value has been stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
' Announce that all thread calls are done.
asyncOpsAreDone.Set()
End If
End Sub
' Helper methods:
' In order to update the TextBlock object, which is on the UI thread, you must
' make a cross-thread call by using the Dispatcher object that is associated
' with the TextBlock. The DisplayOutput helper method and its delegate,
' displayHelper, are used by the BeginInvoke method of the Dispatcher object
' to append text to the TextBlock.
'
Friend Shared Sub Display(ByVal msg As String)
outputBlock.Dispatcher.BeginInvoke(displayHelper, msg)
End Sub
Private Shared displayHelper As New Action(Of String)(AddressOf DisplayOutput)
Private Shared Sub DisplayOutput(ByVal msg As String)
outputBlock.Text &= msg
End Sub
End Class
' This example produces output similar to the following:
'
'Click here to show that the UI is responsive.
'MouseUp.
'Start synchronized resource access (Thread=0)
'Stop synchronized resource access (Thread=0)
'Start synchronized resource access (Thread=1)
'MouseUp.
'Stop synchronized resource access (Thread=1)
'MouseUp.
'MouseUp.
'MouseUp.
'Start synchronized resource access (Thread=2)
'Stop synchronized resource access (Thread=2)
'Start synchronized resource access (Thread=3)
'MouseUp.
'Stop synchronized resource access (Thread=3)
'Start synchronized resource access (Thread=4)
'Stop synchronized resource access (Thread=4)
'All synchronized thread operations have completed.
'
'Start unsynchronized resource access (Thread=1)
'Start unsynchronized resource access (Thread=3)
'Stop unsynchronized resource access (Thread=1)
'Stop unsynchronized resource access (Thread=3)
'Start unsynchronized resource access (Thread=0)
'MouseUp.
'Start unsynchronized resource access (Thread=2)
'Start unsynchronized resource access (Thread=4)
'Stop unsynchronized resource access (Thread=2)
'Stop unsynchronized resource access (Thread=0)
'Stop unsynchronized resource access (Thread=4)
'All unsynchronized thread operations have completed.
'
'Refresh the page to run the demo again.
'MouseUp.
using System;
using System.Threading;
using System.Windows.Input;
// Note: The class whose internal public member is the synchronizing method
// is not public; none of the client code takes a lock on the Resource object.
// The member of the nonpublic class takes the lock on itself. Written this
// way, malicious code cannot take a lock on a public object.
internal class SyncResource
{
public void Access(Int32 threadNum)
{
// Uses Monitor class to enforce synchronization.
lock (this)
{
// The following code makes even-numbered threads yield and
// wait, but because of the lock other threads cannot access
// the resource.
if (threadNum % 2 == 0)
{
Thread.Sleep(2000);
}
Example.Display(
String.Format("Start synchronized resource access (Thread={0})\n",
threadNum));
Thread.Sleep(200);
Example.Display(
String.Format("Stop synchronized resource access (Thread={0})\n",
threadNum));
}
}
}
// Without the lock, the method is called in the order in which
// threads reach it.
internal class UnSyncResource
{
public void Access(Int32 threadNum)
{
// Does not use Monitor class to enforce synchronization.
// The following code makes even-numbered threads yield and
// wait, and the odd-numbered threads can access the resource
// ahead of them.
if (threadNum % 2 == 0)
{
Thread.Sleep(2000);
}
Example.Display(
String.Format("Start unsynchronized resource access (Thread={0})\n",
threadNum));
Thread.Sleep(200);
Example.Display(
String.Format("Stop unsynchronized resource access (Thread={0})\n",
threadNum));
}
}
public class Example
{
private static Int32 numAsyncOps = 5;
private static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
private static SyncResource SyncRes = new SyncResource();
private static UnSyncResource UnSyncRes = new UnSyncResource();
private static Int32 threadNum;
private static System.Windows.Controls.TextBlock outputBlock;
private static bool running = false;
public static void Demo(System.Windows.Controls.TextBlock outputBlock)
{
Example.outputBlock = outputBlock;
outputBlock.Text = "Click here to run the demo.\n";
outputBlock.MouseLeftButtonUp += MouseUp;
}
private static void MouseUp(object sender, MouseButtonEventArgs e)
{
if (running)
{
// Show that the UI is responsive.
outputBlock.Text += "MouseUp.\n";
}
else
{
outputBlock.Text = "Click here to show that the UI is responsive.\n";
// A separate thread must be used to run the demo, so the main
// application thread doesn't block.
Thread t = new Thread(RunDemo);
t.Start();
running = true;
}
}
private static void RunDemo()
{
for (threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool.QueueUserWorkItem(SyncUpdateResource, threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Display("All synchronized operations have completed.\n\n");
// Reset the thread count for unsynchronized calls.
numAsyncOps = 5;
for(threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool.QueueUserWorkItem(UnSyncUpdateResource, threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Display("All unsynchronized thread operations have completed.\n\n");
Display("Refresh the page to run the demo again.\n");
}
// The callback method's signature MUST match that of
// a System.Threading.TimerCallback delegate
// (it takes an Object parameter and returns void).
internal static void SyncUpdateResource(object state)
{
// This calls the internal synchronized method, passing
// a thread number.
SyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, because you cannot know whether another
// thread will access the value before this thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
{
// Announce that all thread calls are done.
asyncOpsAreDone.Set();
}
}
// The callback method's signature MUST match that of
// a System.Threading.TimerCallback delegate
// (it takes an Object parameter and returns void).
internal static void UnSyncUpdateResource(object state)
{
// This calls the unsynchronized method, passing
// a thread number.
UnSyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, because you cannot know whether another
// thread will access the value before this thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
{
// Announce that all thread calls are done.
asyncOpsAreDone.Set();
}
}
// Helper methods:
// In order to update the TextBlock object, which is on the UI thread, you must
// make a cross-thread call by using the Dispatcher object that is associated
// with the TextBlock. The DisplayOutput helper method and its delegate,
// displayHelper, are used by the BeginInvoke method of the Dispatcher object
// to append text to the TextBlock.
//
internal static void Display(string msg)
{
outputBlock.Dispatcher.BeginInvoke(displayHelper, msg);
}
private static Action<string> displayHelper = new Action<string>(DisplayOutput);
private static void DisplayOutput(string msg)
{
outputBlock.Text += msg;
}
}
/* This example produces output similar to the following:
Click here to show that the UI is responsive.
MouseUp.
Start synchronized resource access (Thread=0)
Stop synchronized resource access (Thread=0)
Start synchronized resource access (Thread=1)
MouseUp.
Stop synchronized resource access (Thread=1)
MouseUp.
MouseUp.
MouseUp.
Start synchronized resource access (Thread=2)
Stop synchronized resource access (Thread=2)
Start synchronized resource access (Thread=3)
MouseUp.
Stop synchronized resource access (Thread=3)
Start synchronized resource access (Thread=4)
Stop synchronized resource access (Thread=4)
All synchronized thread operations have completed.
Start unsynchronized resource access (Thread=1)
Start unsynchronized resource access (Thread=3)
Stop unsynchronized resource access (Thread=1)
Stop unsynchronized resource access (Thread=3)
Start unsynchronized resource access (Thread=0)
MouseUp.
Start unsynchronized resource access (Thread=2)
Start unsynchronized resource access (Thread=4)
Stop unsynchronized resource access (Thread=2)
Stop unsynchronized resource access (Thread=0)
Stop unsynchronized resource access (Thread=4)
All unsynchronized thread operations have completed.
Refresh the page to run the demo again.
MouseUp.
*/
See Also