我想知道在属性中正确使用锁。我正在编写一个多线程服务器应用程序,其中吞吐量非常重要。如果我有一个声明的属性:
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还是以上都没有?
感谢。
答案 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;
}
}
}