是否需要锁定对bool的访问权限或者是否为Overkill

时间:2011-06-21 23:54:42

标签: c# multithreading locking readerwriterlockslim

我有一个主要设计为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都不是)。

3 个答案:

答案 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的替代品。