使自定义对象线程安全
本文关键字:安全 线程 对象 自定义 | 更新日期: 2023-09-27 17:54:31
我之前发布了一个关于返回集合的问题,并且出现了线程安全的主题。我得到了这个链接来做更多的阅读,我发现了这一行:
一般来说,避免锁定公共类型或超出您的代码的控制。
首先,纠正我,如果我错了,但不是微软给锁上一个公共类型的例子,平衡变量?
其次,如何锁定自己的getter/setter属性。假设我有以下类:
private int ID;
public Person(int id)
{
this.Identification= id;
}
public int Identification
{
get { return this.ID; }
private set
{
if (value == 0)
{
throw new ArgumentNullException("Must Include ID#");
}
this.ID = value;
}
}
getter是公共的,对吗?只有setter被声明为私有。那么,如何锁定或使getter/setter属性线程安全呢?
你应该在Person
类中定义一个像这样的变量
private readonly object _lock_ = new Object();
如果您想在Person
的所有实例上进行同步,您应该将其设置为static
。
那么当你想要锁定时,你可以这样做
lock(_lock_) //whose there? it's me, I kill you! oops sorry that was knock knock
{
//do what you want
}
我建议你阅读这篇文章:1
当您需要锁定一个变量时,您需要锁定使用该变量的每个位置。锁不是针对变量的,而是针对使用变量的代码区域的。
如果你只在一个地方"读"并不重要——如果你需要为一个变量锁定,你需要在使用该变量的所有地方锁定。
lock
的替代方案是Interlocked
类——它使用处理器级原语进行锁定,速度更快。但是,Interlocked
不能保护多个语句(并且有两个Interlocked
语句与在单个lock
中有这两个语句是不一样的)。
在进行锁定时,必须锁定引用类型的实例(在大多数情况下(但并非总是),该实例也应该是静态实例)。这是为了确保所有锁实际上都是在相同的实例上取出的,而不是它的副本。显然,如果你在不同的地方使用一个副本,你就没有锁定相同的东西,所以你的代码将无法正确序列化。
例如:private static readonly object m_oLock = new object ();
...
lock ( m_oLock )
{
...
}
使用非静态锁是否安全需要对代码进行详细的分析——在某些情况下,它会导致更多的并行性,因为代码的同一区域被锁定的次数较少,但对它的分析可能非常棘手——如果你不确定,就使用static
锁对象。获取一个打开的锁的成本是最小的,但是不正确的分析可能会导致需要花费很长时间来调试的错误。
下面的例子展示了如何锁定属性访问:
private int ID; // do NOT lock on value type instances
private static readonly object Lock = new object ();
public Person(int id)
{
this.Identification = id;
}
public int Identification
{
get
{
lock ( Lock )
{
return this.ID;
}
}
private set
{
if (value == 0)
throw new ArgumentNullException("Must Include ID#");
lock ( Lock )
{
this.ID = value;
}
}
}
因为你的属性只做一个微不足道的get/set操作,你可以尝试使用Interlocked.CompareExchange
而不是一个完整的锁-它会使事情稍微快一些。但是请记住,联锁操作与锁不同。
编辑2:
还有一件事:int
上的一个简单的get/set操作不需要锁——读写32位的值(本身)都是原子的。所以这个例子太简单了—只要您不尝试在应该以原子方式完成的多个操作中使用ID
,那么就不需要锁。但是,如果您的实际代码实际上更复杂,需要检查和设置ID
,那么您可能需要锁定和,您需要锁定组成原子操作的所有操作。这意味着你可能不得不从getter/setter中取出锁——一个变量的get/set对上的两个锁与它们周围的单个锁是不一样的。
关于微软文章的第一个问题的答案:不。本文不锁定balance
变量。它锁定私有thisLock
变量。所以这个例子很好。
其次,根据您发布的代码,您不需要添加任何锁来使您的类线程安全,因为您的数据是不可变的。一旦您创建了Person
的实例并在构造函数中设置了Identification
属性的值,您的类设计就不允许再次更改该属性。这就是不可变性,它本身提供了线程安全性。因此,您不需要费心添加锁之类的东西。同样,假设您的代码示例是准确的。
编辑:这个链接可能对你有用