C# 锁定属性
我想知道在属性中如何正确使用锁。我正在编写一个多线程服务器应用程序,其中吞吐量非常重要。如果我有这样声明的财产:C# 锁定属性,c#,.net,multithreading,concurrency,clr,C#,.net,Multithreading,Concurrency,Clr,我想知道在属性中如何正确使用锁。我正在编写一个多线程服务器应用程序,其中吞吐量非常重要。如果我有这样声明的财产: private DataPoint a; private object aLock = new object(); 最保守的锁似乎如下所示(称之为方法1)。但是,在这种情况下,在初始调用之后的每次调用中,都会有锁的开销: public DataPoint A { get { lock (aLoc
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,还是以上都没有
谢谢。在锁定示例中,您这样做是为了初始化一个值。假设null条件意味着需要初始化该值,则应在获取锁之前和之后检查该值:
if(a == null)
{
lock(aLock)
{
if(a == null)
a = new DataPoint();
}
}
这是因为当一个线程在等待锁时,它在获得锁后要做的工作可能已经由另一个线程完成了。因此,当线程获得锁时,它应该检查工作是否仍然需要完成。在锁定示例中,您这样做是为了初始化一个值。假设null条件意味着需要初始化该值,则应在获取锁之前和之后检查该值:
if(a == null)
{
lock(aLock)
{
if(a == null)
a = new DataPoint();
}
}
这是因为当一个线程在等待锁时,它在获得锁后要做的工作可能已经由另一个线程完成了。因此,当线程获得锁时,它应该检查工作是否仍然需要完成。在锁定之前应该检查null。 如果为null,则锁定,并再次检查null。 如果它仍然为空,则启动数据点并首先将其分配给临时变量。 完成后,将其分配给您的成员并返回
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;
}
}
}
您应该在锁定之前检查null。 如果为null,则锁定,并再次检查null。 如果它仍然为空,则启动数据点并首先将其分配给临时变量。 完成后,将其分配给您的成员并返回
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;
}
}
}
在.NET 4中,您有
System.Lazy
类型来处理这些问题:
class MyClass
{
private readonly Lazy<DataPoint> lazy =
new Lazy<Singleton>(() => new DataPoint());
public DataPoint Instance { get { return lazy.Value; } }
}
class-MyClass
{
私有只读懒惰=
新的延迟(()=>newdatapoint());
公共数据点实例{get{return lazy.Value;}}
}
由于.NET 4中的功能,您可以使用System.Lazy类型来解决以下问题:
class MyClass
{
private readonly Lazy<DataPoint> lazy =
new Lazy<Singleton>(() => new DataPoint());
public DataPoint Instance { get { return lazy.Value; } }
}
class-MyClass
{
私有只读懒惰=
新的延迟(()=>newdatapoint());
公共数据点实例{get{return lazy.Value;}}
}
由于的缘故,第二个示例中的
锁
完全没有意义(没有双关语),但是您是否应该锁定(而不仅仅是有一个合理的初始化值)是另一个问题。为什么不在类构造函数中初始化数据点呢?第二个示例可能是一个有效的场景,取决于数据点的构造函数中发生了什么。我不应该使用“new DataPoint()”来进行说明,而是使用类似DataPoint.Create(“A”)…您正在开发一个服务器,您有锁吗?祝你好运。第二种情况确实有效。如果它已经初始化,您可能不想锁定它。但是,在锁(aLock)之后,您应该(再次)检查a是否为null,否则,如果在未初始化a时执行了对getter的两个并发调用,那么您可能会多次初始化。第二个示例中的锁
完全没有意义(没有双关语),但您是否应该锁定(与仅具有合理的初始化值相反)是另一个问题。为什么不在类构造函数中初始化数据点?第二个示例可能是有效的场景,具体取决于数据点构造函数中发生的情况。我不应该使用“new DataPoint()”出于说明目的,但使用了类似DataPoint.Create(“A”)…的内容。您正在开发一台服务器,并且有锁?祝您好运。第二种情况确实有效。如果已经初始化,您可能不希望锁定。但是,在锁(aLock)之后,您应该(再次)锁定如果A没有初始化,如果两个并发调用被调用,那么一些人会认为双检查锁定实际上是一个反模式,在ECMA CLI内存模型下,它甚至可能被破坏:(第三版本)在java内存模型下,除非引用是不稳定的,否则数据包是原子构造的——对于java来说,这意味着所有字段都是最终的。有些人认为双锁锁定实际上是反模式,在ECMA CLI内存模型下,它甚至可能被破坏:(第三版本)在Java内存模型下,除非引用是易失的或数据点是原子构造的,否则这肯定是不正确的。对于Java来说,这意味着所有字段都是最终字段。请注意,在这种特殊情况下,一个简单的静态只读字段将具有相同的效果。@Sebastian-何时以及如何初始化该字段?如Jon Skeet所述与第4项(或第5项)在同一条中指出,如果您提供静态构造函数,静态字段将在第一次类访问时初始化。@Sebastian我明白了。但是,由于您的注释,我已将post更改为更类似于OP的代码,现在没有任何内容是静态的……请注意,在这种特殊情况下,一个简单的静态只读字段将具有相同的效果。@Sebastian-何时以及如何您可以初始化该字段吗?正如Jon Skeet在与第4项(或第5项)相同的文章中指出的那样,如果您提供静态字段,静态字段将在一级访问上初始化