关于C#:如何避免由于锁定的代码而减速?

How to avoid slowdown due to locked code?

我想知道,即使代码从未执行过,一段锁定的代码如何能够减慢我的代码速度。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void Test_PerformanceUnit()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    Random r = new Random();
    for (int i = 0; i < 10000; i++)
    {
        testRand(r);
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedTicks);
}

public object testRand(Random r)
{
    if (r.Next(1) > 10)
    {
        lock(this) {
            return null;
        }
    }
    return r;
}

这个代码在我的机器上运行大约1300毫秒。如果我们移除锁块(但保留其主体),我们得到750毫秒。几乎是双倍,即使代码从未运行!

当然,这个代码什么也不做。在类中添加一些惰性初始化时,我注意到了这个问题,在这个类中,代码检查对象是否已初始化,是否未初始化。问题是,即使在第一次调用之后,初始化也会被锁定并减慢所有操作。

我的问题是:

  • 为什么会这样?
  • 如何避免减速

  • 关于它发生的原因,评论中已经讨论过:这是由于lock生成的try ... finally的初始化。

    为了避免这种速度减慢,您可以将锁定特性提取到一个新方法中,这样,只有在实际调用该方法时才会初始化锁定机制。

    我用这个简单的代码来尝试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public object testRand(Random r)
    {
        if (r.Next(1) > 10)
        {
            return LockingFeature();
        }
        return r;
    }

    private object LockingFeature()
    {
        lock (_lock)
        {
            return null;
        }
    }

    以下是我的时间(滴答声):

    1
    2
    3
    your code, no lock   : ~500
    your code, with lock : ~1200
    my code              : ~500

    编辑:我的测试代码(比没有锁的代码慢一点)实际上是在静态方法上的,当代码在一个对象的"内部"运行时,时间是相同的。我根据那个确定了时间。