Results
Lock: 85.3 microseconds
Monitor.TryEnter: 11.0 microseconds
Isn't the lock expanded into the same code?
Edit: Results with 1000 iterations: Lock: 103.3 microseconds Monitor.TryEnter: 20.2 microseconds
Code below. Thanks
[Test]
public void Lock_Performance_Test()
{
const int lockIterations = 100;
Stopwatch csLock = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; )
{
lock (object1)
{
i++;
}
}
csLock.Stop();
Stopwatch csMonitor = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; )
{
if (Monitor.TryEnter(object1, TimeSpan.FromSeconds(10)))
{
try
{
i++;
}
finally
{
Monitor.Exit(object1);
}
}
}
csMonitor.Stop();
Console.WriteLine("Lock: {0:f1} microseconds", csLock.Elapsed.Ticks / 10M);
Console.WriteLine("Monitor.TryEnter: {0:f1} microseconds", csMonitor.Elapsed.Ticks / 10M);;
}
I don't actually know the answer, but feel it's important to point out that lock
and Monitor.TryEnter
are not functionally equivalent. From the MSDN documentation on Monitor.TryEnter
:
If successful, this method acquires an exclusive lock on the obj parameter. This method returns immediately, whether or not the lock is available.
The lock
statement is analogous to Monitor.Enter
, which does potentially block. Granted, in your example code, there shouldn't be any blocking issues; but I would wager that since lock
provides blocking, it does a little more work (potentially) than TryEnter
does.
For what it's worth, I just tried your code on my machine and got completely different results:
100 iterations:lock
: 4.4 microsecondsMonitor.TryEnter
: 16.1 microsecondsMonitor.Enter
: 3.9 microseconds
100000 iterations:lock
: 2872.5 microsecondsMonitor.TryEnter
: 5226.6 microsecondsMonitor.Enter
: 2432.9 microseconds
This seriously undermines my initial guess and shows that, on my system, lock
(which performs about the same as Monitor.Enter
) actually drastically outperforms Monitor.TryEnter
.
Indeed, I attempted this in VS 2010 targeting both .NET 3.5 and .NET 4.0 and, though the results were different, in each case lock
did in fact outperform Monitor.TryEnter
:
Ran 100 times, 100000 iterations each time:
Lock: 279736.4 microseconds
Monitor.TryEnter: 1366751.5 microseconds
Monitor.TryEnter (no timeout): 475107.3 microseconds
Monitor.Enter: 332334.1 microseconds
Ran 100 times, 100000 iterations each time:
Lock: 334273.7 microseconds
Monitor.TryEnter: 1671363.4 microseconds
Monitor.TryEnter (no timeout): 531451.8 microseconds
Monitor.Enter: 316693.1 microseconds
(Notice I also tested Monitor.TryEnter
with no timeout, as I agreed with Marc that calling TimeSpan.FromSeconds
was almost certainly slowing down your times for Monitor.TryEnter
--and these tests support that--though it's strange, since in your case apparently lock
is still significantly slower.)
Based on these results I am strongly inclined to believe that your measured execution times are somehow affected by running this code with the Test
attribute. Either that or this code is far more machine-dependent than I would have expected.
100 is far too few, and running in a test framework may skew things. It is also possibly (see comments) related to any additional cost associated with the first lock against an object; try:
Also, note that in 4.0 lock
is not Monitor.Enter(object)
- so expect different results in 4.0.
But I get:
lock: 3548ms
Monitor.TryEnter: 7008ms
Monitor.TryEnter (2): 2947ms
Monitor.Enter: 2906ms
From the test rig:
using System;
using System.Diagnostics;
using System.Threading;
static class Program {
static void Main()
{
const int lockIterations = 50000000;
object object1 = new object();
lock (object1) { Console.WriteLine("First one has to pay an extra toll"); }
Stopwatch csLock = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; ) {
lock (object1) { i++; }
}
csLock.Stop();
Console.WriteLine("lock: " + csLock.ElapsedMilliseconds + "ms");
Stopwatch csMonitorTryEnter = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; ) {
if (Monitor.TryEnter(object1, TimeSpan.FromSeconds(10))) {
try { i++; } finally { Monitor.Exit(object1); }
}
}
csMonitorTryEnter.Stop();
Console.WriteLine("Monitor.TryEnter: " + csMonitorTryEnter.ElapsedMilliseconds + "ms");
csMonitorTryEnter = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; ) {
if (Monitor.TryEnter(object1, 10000)) {
try { i++; } finally { Monitor.Exit(object1); }
}
}
csMonitorTryEnter.Stop();
Console.WriteLine("Monitor.TryEnter (2): " + csMonitorTryEnter.ElapsedMilliseconds + "ms");
Stopwatch csMonitorEnter = Stopwatch.StartNew();
for (int i = 0; i < lockIterations; ) {
Monitor.Enter(object1);
try { i++; } finally { Monitor.Exit(object1); }
}
csMonitorEnter.Stop();
Console.WriteLine("Monitor.Enter: " + csMonitorEnter.ElapsedMilliseconds + "ms");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With