锁定属性

时间:2012-09-24 21:56:10

标签: c# .net multithreading concurrency clr

我想知道在属性中正确使用锁。我正在编写一个多线程服务器应用程序,其中吞吐量非常重要。如果我有一个声明的属性:

    private DataPoint a;
    private object aLock = new object();

最保守的锁似乎如下(称之为方法1)。但是,在这种情况下,在初始调用之后的每次调用中,都会有锁的开销:

    public DataPoint A
    {
        get
        {
            lock (aLock)
            {
                if (a == null)
                {
                    a = new DataPoint();
                }

                return a;
            }
        }
    }

或者,我应该将锁定移动到设置“a”的行(称之为方法2)。在这种情况下,有可能多次设置“a”(这是好的),但一旦设置,就没有锁的开销。

    public DataPoint A
    {
        get
        {
            if (a == null)
            {
                lock(aLock)
                {
                    a = new DataPoint();
                }
            }

            return a;
        }
    }

锁定对属性的并发访问的推荐方法是什么?它是方法1,方法2还是以上都没有?

感谢。

3 个答案:

答案 0 :(得分:7)

在.NET 4中,您有System.Lazy<T>类型可以解决这些问题:

class MyClass
{
    private readonly Lazy<DataPoint> lazy =
        new Lazy<Singleton>(() => new DataPoint());

    public DataPoint Instance { get { return lazy.Value; } }
} 

Jon Skeet提供

答案 1 :(得分:2)

在您的锁定示例中,您正在这样做以初始化值。假设null条件意味着需要初始化值,您应该在获取锁之后在之前检查它:

if(a == null)
{
  lock(aLock)
  {
    if(a == null)
      a = new DataPoint();
  }
}

这样做的原因是,当一个线程正在等待锁定时,一旦获得锁定它将要执行的工作有可能是由另一个线程完成的。因此,当线程获得锁定时,它应检查是否仍需要完成工作。

答案 2 :(得分:1)

你应该在锁定之前检查null。 如果为null,则锁定,并再次检查null。 如果它仍为null,则启动DataPoint并首先将其分配给临时变量。 完成后,将其分配给您的会员并将其退回。

private DataPoint _dataPoint;

public DataPoint A
{
    get
    {
        if(_dataPoint != null)
            return _dataPoint;

        lock (aLock)
        {
            if (_dataPoint == null)
            {
                var dataPoint = new DataPoint();
                // do more stuff with dataPoint
                _dataPoint = dataPoint;
            }

            return _dataPoint;
        }
    }
}