【问题标题】:Locks in properties锁定属性
【发布时间】:2012-09-24 21:56:10
【问题描述】:

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

    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;
        }
    }

锁定对属性的并发访问的推荐方法是什么?是方法一、方法二还是以上都不是?

谢谢。

【问题讨论】:

  • 第二个例子中的lock 完全没有意义(没有双关语),但是你是否应该锁定(而不是仅仅拥有一个合理的初始化值)是一个不同的问题。
  • 为什么不在类构造函数中初始化你的DataPoint?
  • 第二个例子可能是一个有效的场景,这取决于 DataPoint 的构造函数中发生了什么。出于说明目的,我不应该使用“new DataPoint()”,而是使用类似 DataPoint.Create("A")...
  • 您正在开发服务器并且您有锁?祝你好运。
  • 第二种情况确实有效。如果它已经被初始化,你可能不想锁定它。但是,在 lock(aLock) 之后,您应该(再次)检查 a 是否为空,否则如果在 a 未初始化时执行了对 getter 的两个并发调用,您可能最终会初始化多次。

标签: c# .net multithreading concurrency clr


【解决方案1】:

在 .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

【讨论】:

  • 请注意,在这种特殊情况下,一个简单的静态只读字段将具有相同的效果。
  • @Sebastian - 您将何时以及如何初始化该字段?
  • 正如 Jon Skeet 在与第 4 项(或第 5 项)相同的文章中指出的那样,如果您提供静态构造函数,则会在第一个类访问时初始化静态字段。
  • @Sebastian 我明白了。但是,自从您发表评论以来,我已将帖子更改为与 OP 的代码更相似,现在没有什么是静态的...
【解决方案2】:

在您的锁定示例中,您这样做是为了初始化一个值。假设 null 条件意味着需要初始化该值,您应该在 之前和 之前检查它,并在获得锁之后:

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

这样做的原因是,当一个线程正在等待锁时,它在获得锁后将要执行的工作有可能已经由另一个线程完成。所以当线程获得锁时,它应该检查是否还需要完成工作。

【讨论】:

【解决方案3】:

您应该在锁定之前检查 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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多