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