为什么锁比Monitor.TryEnter慢得多?

Cod*_*der 21 c#

结果

锁定:85.3微秒

Monitor.TryEnter:11.0微秒

锁是否扩展为相同的代码?

编辑:1000次迭代的结果:锁定:103.3微秒Monitor.TryEnter:20.2微秒

代码如下.谢谢

    [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);;
    }
Run Code Online (Sandbox Code Playgroud)

Dan*_*Tao 31

我实际上并不知道答案,但觉得指出这一点lock并且Monitor.TryEnter在功能上并不相同是很重要的.从MSDN文档Monitor.TryEnter:

如果成功,则此方法获取obj参数的独占锁定.无论锁是否可用,此方法都会立即返回.

lock语句类似于Monitor.Enter,这不会潜在地阻塞.当然,在您的示例代码中,不应存在任何阻塞问题; 但我会打赌,因为lock提供阻止,它做的(可能)比做更多的工作TryEnter.


为了它的价值,我只是在我的机器上尝试了你的代码并获得了完全不同的结果:

100次迭代::
lock4.4微秒
Monitor.TryEnter:16.1微秒
Monitor.Enter:3.9微秒

100000次迭代::
lock2872.5微秒
Monitor.TryEnter:5226.6微秒
Monitor.Enter:2432.9微秒

这严重破坏了我最初的猜测,并表明,在我的系统上,lock(其性能大致相同Monitor.Enter)实际上表现得非常好 Monitor.TryEnter.


实际上,我在针对.NET 3.5和.NET 4.0的VS 2010中尝试了这一点,尽管结果不同,但在每种情况下lock确实都表现优异Monitor.TryEnter:

运行时版本:2.0.50727.3603

运行100次,每次100000次迭代:
锁定:279736.4微秒
Monitor.TryEnter:1366751.5微秒
Monitor.TryEnter(无超时):475107.3微秒
Monitor.Enter:332334.1微秒

运行时版本:4.0.30128.1

运行100次,每次100000次迭代:
锁定:334273.7微秒
Monitor.TryEnter:1671363.4微秒
Monitor.TryEnter(无超时):531451.8微秒
Monitor.Enter:316693.1微秒

(请注意,我还测试了Monitor.TryEnter,没有超时,因为我与马克认为,呼吁TimeSpan.FromSeconds几乎肯定放慢您的时间Monitor.TryEnter-并且这些测试支持-尽管这很奇怪,因为在你的情况下,显然lock依然.显著慢)

基于这些结果,我强烈倾向于认为通过使用Test属性运行此代码会影响您的测量执行时间.无论是那个代码还是这个代码都比我预期的更依赖于机器.


Mar*_*ell 5

100太少了,在测试框架中运行可能会使内容歪斜。这也可能(参见注释)与与对对象的第一次锁定相关的任何额外费用有关;尝试:

  • 首先在循环外锁定一次
  • 做更多的迭代
  • 在控制台exe中,在命令行中,在释放模式下

另外,请注意,在4.0 lock不是 Monitor.Enter(object) -因此期望在4.0中获得不同的结果。

但是我得到:

lock: 3548ms
Monitor.TryEnter: 7008ms
Monitor.TryEnter (2): 2947ms
Monitor.Enter: 2906ms
Run Code Online (Sandbox Code Playgroud)

从测试台:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)