我有一个主要设计为POCO类的类,各种线程和任务可以读取其值,只有其他只偶尔更新这些值。这似乎是ReaderWriterLockSlim的理想场景。
问题是,在课堂上,如果属性需要是线程安全的,如果属性是bool,那是否过度?如果它是一个int会发生什么?日期时间?
public class MyClass
{
private bool _theValue = false;
private ReaderWriterLockSlim _theValueLock = new ReaderWriterLockSlim();
public bool TheValue
{
get
{
bool returnVal = false;
try
{
_theValueLock.EnterReadLock();
returnVal = _theValue;
}
finally
{ _theValueLock.ExitReadLock(); }
return returnVal;
}
set
{
try
{
_theValueLock.EnterWriteLock();
_theValue = value;
}
finally
{ _theValueLock.ExitWriteLock(); }
}
}
}
所有这些代码都是过度的,而且很简单......
public bool TheValue { get; set; }
......就足够了? 因为Type是bool,安全吗?如果是的话,什么时候变得不安全? 字节?诠释?日期时间?
修改
我的基本架构是具有此类存储状态。也许有一个服务负责对这个类进行写操作。所有其他类都可以根据此状态数据读取和执行其逻辑。我会尽力确保所有数据都是一致的,但如下所述,我主要担心的是数据的原子性和危险性。
结论
感谢大家的回应,一切都很有价值。我主要担心的是写/读的原子性(即担心松弛)。对于.NET平台,如果有问题的变量是一个小于4个字节的内置值类型,那么读取和写入都是原子的(例如,short和int都很好,long和double都不是)。
答案 0 :(得分:8)
根据使用方式的不同,您可能需要标记布尔值volatile。这将需要您的财产的支持领域。
您现在不需要使用ReaderWriterLockSlim
处理此问题,因为它小于32位(假设您正在使用AutoLayout,有关详细信息,请参阅this post或最详细的,ECMA 335规范中标题为内存访问的原子性的部分。如果您使用的是大于此类型的类型,则需要进行某种形式的同步。
我建议:
public class MyClass
{
private volatile bool _theValue = false;
public bool TheValue
{
get { return _theValue; }
set { _theValue = value; }
}
}
答案 1 :(得分:4)
没有类型是真正安全的! 更确切地说,C#规范确保读取或分配小于4个字节的结构类型或引用是原子的。如果你的操作系统是64位,那么通过为小于8字节的结构确保相同的东西,CLR会做得更好。
但是,如果你不小心的话,任何比分配或读取值更复杂的事情都可能被另一个竞争线程打断。
即使是这样简单的事情:
myBool = !myBool
如果竞争线程修改myBool的值,会得到意外的结果。
如果你想确保没有发生类似的事情,建议使用锁。除非您确切知道自己在做什么,否则强烈建议不要使用volatile关键字。查看these blog posts了解其他信息。
但是在您的示例中,除了单个写入或单个读取之外,该属性不执行任何操作,因此锁定是无用的。但如果有任何额外的治疗方法就不会这样。
答案 2 :(得分:4)
您有几个选择。
lock
。volatile
。Interlocked
方法集。由于对bool
的读写保证是原子的,因此您可能不需要做任何事情。这在很大程度上取决于你的课程使用方式的性质。原子性不等于线程安全性。这只是它的一个方面。
理想的解决方案是使您的类不可变。不可变类通常是线程安全的,因为它们不能被其他线程修改(或根本不能修改)。有时这只是不可行。
我在列表中的下一个偏好是普通的lock
。获取和释放的开销非常小。事实上,我认为你会发现lock
会在大多数情况下击败ReaderWriterLockSlim
,特别是如果你所做的只是读/写一个变量。我自己的个人测试表明RWLS的开销比lock
大约5倍慢。因此,除非读取操作异常长并且它们明显超过写入操作,否则RWLS将无济于事。
如果您担心锁定开销,那么一定要将字段标记为volatile
。请记住volatile
不是解决所有并发问题的神奇子弹。它不是lock
的替代品。