Monitor.Enter Method (Object)
Microsoft Silverlight will reach end of support after October 2021. Learn more.
Acquires an exclusive lock on the specified object.
Namespace: System.Threading
Assembly: mscorlib (in mscorlib.dll)
Syntax
'Declaration
<SecuritySafeCriticalAttribute> _
Public Shared Sub Enter ( _
obj As Object _
)
[SecuritySafeCriticalAttribute]
public static void Enter(
Object obj
)
Parameters
- obj
Type: System.Object
The object on which to acquire the monitor lock.
Exceptions
Exception | Condition |
---|---|
ArgumentNullException | The obj parameter is nulla null reference (Nothing in Visual Basic). |
Remarks
Use Enter to acquire the Monitor on the object passed as the parameter. If another thread has executed an Enter on the object, but has not yet executed the corresponding Exit, the current thread will block until the other thread releases the object. It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.
Use Monitor to lock objects (that is, reference types), not value types. When you pass a value type variable to Enter, it is boxed as an object. If you pass the same variable to Enter again, it is boxed as a separate object, and the thread does not block. The code that Monitor is supposedly protecting is not protected. Furthermore, when you pass the variable to Exit, still another separate object is created. Because the object passed to Exit is different from the object passed to Enter, Monitor throws SynchronizationLockException. For details, see the conceptual topic Monitors.
Use a C# try…finally block (Try…Finally in Visual Basic) to ensure that you release the monitor, or use the C# lock statement (SyncLock in Visual Basic), which wraps the Exit method in a try…finally block.
Examples
The following example demonstrates how to use the Enter, TryEnter, TryEnter, and Exit methods. The example defines a generic SafeQueue class that protects a private Queue<T> by using the methods of Monitor.
Important Note: |
---|
When you acquire a lock, always use try/finally to ensure that the lock is released even if an exception is thrown. For example, the Dequeue method of this example throws an exception if the queue is empty. The method that acquires the lock must be outside the try block so that an exception in that method does not cause the finally block to execute. The C# lock and Visual Basic SyncLock statements are implemented using the Enter and Exit methods. We recommend that you use these statements instead of the Enter and Exit methods, because lock and SyncLock always use try/finally blocks to protect the Monitor. |
The example creates a SafeQueue<int> (SafeQueue(Of Integer) in Visual Basic) and starts three threads that randomly queue and dequeue integers. When all three threads are finished, the example prints statistics for each operation.
Note: |
---|
To run this example, see Building Examples That Use a Demo Method and a TextBlock Control. |
Imports System.Threading
Imports System.Collections.Generic
Imports System.Text
Class SafeQueue(Of T)
' A queue that is protected by Monitor.
Private m_inputQueue As New Queue(Of T)
' Lock the queue and add an element.
Public Sub Enqueue(ByVal qValue As T)
' Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue)
Try
' When the lock is obtained, add an element.
m_inputQueue.Enqueue(qValue)
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
End Sub
' Try to add an element to the queue: Add the element to the queue
' only if the lock is immediately available.
Public Function TryEnqueue(ByVal qValue As T) As Boolean
' Request the lock.
If Monitor.TryEnter(m_inputQueue) Then
Try
m_inputQueue.Enqueue(qValue)
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
Return True
Else
Return False
End If
End Function
' Try to add an element to the queue: Add the element to the queue
' only if the lock becomes available during the specified time
' interval.
Public Function TryEnqueue(ByVal qValue As T, ByVal waitTime As Integer) As Boolean
' Request the lock.
If Monitor.TryEnter(m_inputQueue, waitTime) Then
Try
m_inputQueue.Enqueue(qValue)
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
Return True
Else
Return False
End If
End Function
' Lock the queue and dequeue an element.
Public Function Dequeue() As T
Dim retval As T
' Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue)
Try
' When the lock is obtained, dequeue an element.
retval = m_inputQueue.Dequeue()
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
Return retval
End Function
' Delete all elements that equal the given object.
Public Function Remove(ByVal qValue As T) As Integer
Dim removedCt As Integer = 0
' Wait until the lock is available and lock the queue.
Monitor.Enter(m_inputQueue)
Try
Dim counter As Integer = m_inputQueue.Count
While (counter > 0)
'Check each element.
Dim elem As T = m_inputQueue.Dequeue()
If Not elem.Equals(qValue) Then
m_inputQueue.Enqueue(elem)
Else
' Keep a count of items removed.
removedCt += 1
End If
counter = counter - 1
End While
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
Return removedCt
End Function
' Print all queue elements.
Public Function PrintAllElements() As String
Dim output As New StringBuilder()
'Lock the queue.
Monitor.Enter(m_inputQueue)
Try
For Each elem As T In m_inputQueue
' Print the next element.
output.AppendLine(elem.ToString())
Next
Finally
' Ensure that the lock is released.
Monitor.Exit(m_inputQueue)
End Try
Return output.ToString()
End Function
End Class
Public Class Example
Private Shared outputBlock As System.Windows.Controls.TextBlock
Private Shared q As New SafeQueue(Of Integer)
Private Shared threadsRunning As Integer = 0
Private Shared results(2)() As Integer
Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)
outputBlock.FontFamily = New FontFamily("Courier New")
outputBlock.Text = "Working..." & vbLf
Example.outputBlock = outputBlock
For i As Integer = 0 To 2
Dim t As New Thread(AddressOf ThreadProc)
t.Start(i)
Interlocked.Increment(threadsRunning)
Next i
End Sub
Private Shared Sub ThreadProc(ByVal state As Object)
Dim finish As DateTime = DateTime.Now.AddSeconds(10)
Dim rand As New Random()
Dim result() As Integer = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
Dim threadNum As Integer = CInt(state)
While (DateTime.Now < finish)
Dim what As Integer = rand.Next(250)
Dim how As Integer = rand.Next(100)
If how < 16 Then
q.Enqueue(what)
result(ThreadResultIndex.EnqueueCt) += 1
Else If how < 32 Then
If q.TryEnqueue(what)
result(ThreadResultIndex.TryEnqueueSucceedCt) += 1
Else
result(ThreadResultIndex.TryEnqueueFailCt) += 1
End If
Else If how < 48 Then
' Even a very small wait significantly increases the success
' rate of the conditional enqueue operation.
If q.TryEnqueue(what, 10)
result(ThreadResultIndex.TryEnqueueWaitSucceedCt) += 1
Else
result(ThreadResultIndex.TryEnqueueWaitFailCt) += 1
End If
Else If how < 96 Then
result(ThreadResultIndex.DequeueCt) += 1
Try
q.Dequeue()
Catch
result(ThreadResultIndex.DequeueExCt) += 1
End Try
Else
result(ThreadResultIndex.RemoveCt) += 1
result(ThreadResultIndex.RemovedCt) += q.Remove(what)
End If
End While
results(threadNum) = result
If 0 = Interlocked.Decrement(threadsRunning) Then
Dim sb As New StringBuilder( _
" Thread 1 Thread 2 Thread 3 Total" & vbLf)
For row As Integer = 0 To 8
Dim total As Integer = 0
sb.Append(titles(row))
For col As Integer = 0 To 2
sb.Append(String.Format("{0,9}", results(col)(row)))
total += results(col)(row)
Next col
sb.AppendLine(String.Format("{0,9}", total))
Next row
outputBlock.Dispatcher.BeginInvoke(displayHelper, sb.ToString())
End If
End Sub
Private Shared titles() As String = { _
"Enqueue ", _
"TryEnqueue succeeded ", _
"TryEnqueue failed ", _
"TryEnqueue(T, wait) succeeded ", _
"TryEnqueue(T, wait) failed ", _
"Dequeue attempts ", _
"Dequeue exceptions ", _
"Remove operations ", _
"Queue elements removed " _
}
Private Enum ThreadResultIndex
EnqueueCt
TryEnqueueSucceedCt
TryEnqueueFailCt
TryEnqueueWaitSucceedCt
TryEnqueueWaitFailCt
DequeueCt
DequeueExCt
RemoveCt
RemovedCt
End Enum
' 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.
'
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:
'
'Working...
' Thread 1 Thread 2 Thread 3 Total
'Enqueue 65947 108269 71071 245287
'TryEnqueue succeeded 66084 108631 71218 245933
'TryEnqueue failed 105 168 130 403
'TryEnqueue(T, wait) succeeded 66658 108973 71695 247326
'TryEnqueue(T, wait) failed 2 2 1 5
'Dequeue attempts 199390 326435 214917 740742
'Dequeue exceptions 3508 5380 3929 12817
'Remove operations 16591 27104 17924 61619
'Queue elements removed 2921 4690 2982 10593
using System;
using System.Threading;
using System.Collections.Generic;
using System.Text;
class SafeQueue<T>
{
// A queue that is protected by Monitor.
private Queue<T> m_inputQueue = new Queue<T>();
// Lock the queue and add an element.
public void Enqueue(T qValue)
{
// Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue);
try
{
// When the lock is obtained, add an element.
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
}
// Try to add an element to the queue: Add the element to the queue
// only if the lock is immediately available.
public bool TryEnqueue(T qValue)
{
// Request the lock.
if (Monitor.TryEnter(m_inputQueue))
{
try
{
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return true;
}
else
{
return false;
}
}
// Try to add an element to the queue: Add the element to the queue
// only if the lock becomes available during the specified time
// interval.
public bool TryEnqueue(T qValue, int waitTime)
{
// Request the lock.
if (Monitor.TryEnter(m_inputQueue, waitTime))
{
try
{
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return true;
}
else
{
return false;
}
}
// Lock the queue and dequeue an element.
public T Dequeue()
{
T retval;
// Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue);
try
{
// When the lock is obtained, dequeue an element.
retval = m_inputQueue.Dequeue();
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return retval;
}
// Delete all elements that equal the given object.
public int Remove(T qValue)
{
int removedCt = 0;
// Wait until the lock is available and lock the queue.
Monitor.Enter(m_inputQueue);
try
{
int counter = m_inputQueue.Count;
while (counter>0)
//Check each element.
{
T elem = m_inputQueue.Dequeue();
if (!elem.Equals(qValue))
{
m_inputQueue.Enqueue(elem);
}
else
{
// Keep a count of items removed.
removedCt += 1;
}
counter = counter-1;
}
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return removedCt;
}
// Print all queue elements.
public string PrintAllElements()
{
StringBuilder output = new StringBuilder();
//Lock the queue.
Monitor.Enter(m_inputQueue);
try
{
foreach( T elem in m_inputQueue )
{
// Print the next element.
output.AppendLine(elem.ToString());
}
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return output.ToString();
}
}
public class Example
{
private static System.Windows.Controls.TextBlock outputBlock;
private static SafeQueue<int> q = new SafeQueue<int>();
private static int threadsRunning = 0;
private static int[][] results = new int[3][];
public static void Demo(System.Windows.Controls.TextBlock outputBlock)
{
outputBlock.FontFamily = new System.Windows.Media.FontFamily("Courier New");
outputBlock.Text = "Working...\n";
Example.outputBlock = outputBlock;
for(int i = 0; i < 3; i++)
{
Thread t = new Thread(ThreadProc);
t.Start(i);
Interlocked.Increment(ref threadsRunning);
}
}
private static void ThreadProc(object state)
{
DateTime finish = DateTime.Now.AddSeconds(10);
Random rand = new Random();
int[] result = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int threadNum = (int) state;
while (DateTime.Now < finish)
{
int what = rand.Next(250);
int how = rand.Next(100);
if (how < 16)
{
q.Enqueue(what);
result[(int)ThreadResultIndex.EnqueueCt] += 1;
}
else if (how < 32)
{
if (q.TryEnqueue(what))
{
result[(int)ThreadResultIndex.TryEnqueueSucceedCt] += 1;
}
else
{
result[(int)ThreadResultIndex.TryEnqueueFailCt] += 1;
}
}
else if (how < 48)
{
// Even a very small wait significantly increases the success
// rate of the conditional enqueue operation.
if (q.TryEnqueue(what, 10))
{
result[(int)ThreadResultIndex.TryEnqueueWaitSucceedCt] += 1;
}
else
{
result[(int)ThreadResultIndex.TryEnqueueWaitFailCt] += 1;
}
}
else if (how < 96)
{
result[(int)ThreadResultIndex.DequeueCt] += 1;
try
{
q.Dequeue();
}
catch
{
result[(int)ThreadResultIndex.DequeueExCt] += 1;
}
}
else
{
result[(int)ThreadResultIndex.RemoveCt] += 1;
result[(int)ThreadResultIndex.RemovedCt] += q.Remove(what);
}
}
results[threadNum] = result;
if (0 == Interlocked.Decrement(ref threadsRunning))
{
StringBuilder sb = new StringBuilder(
" Thread 1 Thread 2 Thread 3 Total\n");
for(int row = 0; row < 9; row++)
{
int total = 0;
sb.Append(titles[row]);
for(int col = 0; col < 3; col++)
{
sb.Append(String.Format("{0,9}", results[col][row]));
total += results[col][row];
}
sb.AppendLine(String.Format("{0,9}", total));
}
outputBlock.Dispatcher.BeginInvoke(displayHelper, sb.ToString());
}
}
private static string[] titles = {
"Enqueue ",
"TryEnqueue succeeded ",
"TryEnqueue failed ",
"TryEnqueue(T, wait) succeeded ",
"TryEnqueue(T, wait) failed ",
"Dequeue attempts ",
"Dequeue exceptions ",
"Remove operations ",
"Queue elements removed "};
private enum ThreadResultIndex
{
EnqueueCt,
TryEnqueueSucceedCt,
TryEnqueueFailCt,
TryEnqueueWaitSucceedCt,
TryEnqueueWaitFailCt,
DequeueCt,
DequeueExCt,
RemoveCt,
RemovedCt
};
// 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.
//
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:
Working...
Thread 1 Thread 2 Thread 3 Total
Enqueue 65947 108269 71071 245287
TryEnqueue succeeded 66084 108631 71218 245933
TryEnqueue failed 105 168 130 403
TryEnqueue(T, wait) succeeded 66658 108973 71695 247326
TryEnqueue(T, wait) failed 2 2 1 5
Dequeue attempts 199390 326435 214917 740742
Dequeue exceptions 3508 5380 3929 12817
Remove operations 16591 27104 17924 61619
Queue elements removed 2921 4690 2982 10593
*/
Version Information
Silverlight
Supported in: 5, 4, 3
Silverlight for Windows Phone
Supported in: Windows Phone OS 7.1, Windows Phone OS 7.0
XNA Framework
Supported in: Xbox 360, Windows Phone OS 7.0
Platforms
For a list of the operating systems and browsers that are supported by Silverlight, see Supported Operating Systems and Browsers.
See Also