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


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


12512
2018-06-21 23:54


起源

不保护它是非常沉重的杀戮。如果不显示属性的使用方式,则无法获得准确的答案。在物业层锁定很少有效。 - Hans Passant
更多我自己参考的一些链接 链接 线程的最佳实践, 链接 由于@brian,atmoicity,voltile, 链接 由于@reed,快速阅读原子性 - Andre
作为个人笔记,听dnr.tv和jon双向飞碟对锁定bool的评论,我抬头看了这个 askjonskeet.com/answer/6873994/... 基本上说,在多线程场景中,锁定它可能是一个好主意。 - Andre


答案:


你有几个选择。

  • 没做什么。
  • 使类不可变。
  • 使用普通的旧 lock
  • 将字段标记为 volatile
  • 使用 Interlocked 一套方法。

自从读写到 bool 保证是原子的,那么你可能不需要做任何事情。这在很大程度上取决于你的课程使用方式的性质。原子性不等于线程安全性。这只是它的一个方面。

理想的解决方案是使您的类不可变。不可变类通常是线程安全的,因为它们不能被其他线程修改(或根本不能修改)。有时这只是不可行的。

我在列表中的下一个偏好是一个简单的旧 lock。获取和释放的开销非常小。事实上,我认为你会发现一个 lock 将击败一个 ReaderWriterLockSlim 在大多数情况下,特别是如果您所做的只是读/写变量。我自己的个人测试表明RWLS的开销约为5倍 比较慢 比一个 lock。因此,除非读取操作异常长并且它们明显超过写入操作,否则RWLS将无济于事。

如果您担心锁定开销,那么一定要将字段标记为 volatile。记住这一点 volatile 不是解决所有并发问题的神奇子弹。它不是作为替代品 lock


4
2018-06-22 01:40





根据使用方式的不同,您可能需要标记布尔值 挥发物。这将需要您的财产的支持领域。

你不应该用这个来处理这个问题 ReaderWriterLockSlim正如你现在所做的那样,因为它小于32位(假设您正在使用AutoLayout,有关详细信息,请参阅 这个帖子 或者,最详细的,标题为的部分 记忆访问的原子性 在ECMA 335规范中)。如果您使用的是大于此类型的类型,则需要某种形式的同步。

我建议:

public class MyClass
{
    private volatile bool _theValue = false;
    public bool TheValue 
    {
        get { return _theValue; } 
        set { _theValue = value; } 
    }
 }

8
2018-06-22 00:00



如您所知,访问a bool 已经原子化了。但 volatile 不会提供任何同步或线程安全。 - user7116
@sixlettervariables:是的,但是OP锁定单个属性的解决方案仅对保证原子性有用 - 原始解决方案中也没有同步或线程安全性。这是一个完全可以接受的替代品 在这种情况下。 - Reed Copsey
很公平。虽然他使用锁的原始解决方案将使用RWLS提供线程安全访问。 - user7116
据埃里克说,一个不稳定的领域是一个坏主意,他更喜欢 lock: blogs.msdn.microsoft.com/ericlippert/2011/06/16/... (“我不鼓励你做一个不稳定的领域。”) - Tim Schmelter


没有类型是真正安全的! 更确切地说,C#规范确保读取或分配小于4个字节的结构类型或引用是原子的。如果你的操作系统是64位,那么通过为小于8字节的结构确保相同的东西,CLR会做得更好。

但是如果你不小心的话,任何比分配或读取值更复杂的东西都可能被另一个竞争线程打断。

即使是这样简单的事情:

myBool = !myBool

如果竞争线程修改myBool的值,则会得到意外的结果。

如果您想确保不会发生类似的事情,建议使用锁。除非您确切知道自己在做什么,否则强烈建议不要使用volatile关键字。检查 这些  博客  帖子 用于附加信息。

但是,在您的示例中,除了单个写入或单个读取之外,该属性不执行任何操作,因此锁定无用。但是如果有任何额外的治疗方法就不会这样。


4
2018-06-22 00:08



充实你的榜样。在您的示例中,您同时读取写入,而myBool = true仅写入。因此,对于myBool =!myBool,volatile是不够的 - liorafar