【问题标题】:Is locking access to a bool required or is it Overkill是否需要锁定对布尔值的访问,或者是否过度杀伤
【发布时间】:2011-06-21 23:54:42
【问题描述】:

我有一个主要设计为 POCO 类的类,各种线程和任务可以读取它的值,只有其他人只是偶尔更新这些值。这似乎是 ReaderWriterLockSlim 的理想方案。

问题是,在类中,如果需要线程安全的属性,如果属性是布尔值,那是不是矫枉过正?如果它是一个 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 不行)。

【问题讨论】:

  • 不保护它是非常严重的杀戮。如果不显示该属性的使用方式,您将无法获得准确的答案。在属性级别锁定很少起作用。
  • 更多供我参考 一些链接 link 线程最佳实践,link atmoicity,voltile 感谢@brian,link 快速阅读原子性感谢@reed
  • 作为个人笔记,听了 dnr.tv 和 jon skeet 对锁定布尔值的评论,我查阅了这个 askjonskeet.com/answer/6873994/…,它基本上说,在多线程场景中,这可能是个好主意锁定它。

标签: c# multithreading locking readerwriterlockslim


【解决方案1】:

根据使用方式,您可能需要标记布尔值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; } 
    }
 }

【讨论】:

  • 正如您所指出的,对bool 的访问已经是原子的。但是volatile 不会提供任何同步或线程安全。
  • @sixlettervariables: 是的,但是 OP 仅锁定单个属性的解决方案对于保证原子性很有用 - 原始解决方案中也没有同步或线程安全。这是一个完全可以接受的替代在这种情况下
  • 很公平。尽管他最初的带锁解决方案将使用 RWLS 提供线程安全访问。
  • Eric 认为 volatile 字段是个坏主意,他更喜欢 lockblogs.msdn.microsoft.com/ericlippert/2011/06/16/…“我不鼓励你创建 volatile 字段。”
【解决方案2】:

你有几个选择。

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

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

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

我在列表中的下一个偏好是普通的旧 lock。获取和释放的开销非常小。事实上,我想你会发现在大多数情况下lock 会击败ReaderWriterLockSlim,尤其是当你所做的只是读/写一个变量时。我自己的个人测试表明,RWLS 的开销比lock 慢 5 倍。因此,除非读取操作异常长且数量明显多于写入操作,否则 RWLS 将无济于事。

如果您担心锁开销,请务必将字段标记为volatile。请记住,volatile 并不是解决所有并发问题的灵丹妙药。它不能替代lock

【讨论】:

    【解决方案3】:

    没有类型是真正安全的! 更准确地说,C# 规范向您保证读取或分配少于 4 个字节的结构类型或引用是原子的。如果你的操作系统是 64 位的,CLR 会更好一点,因为它可以确保对小于 8 字节的结构做同样的事情。

    但是,如果您不小心,任何比赋值或读取值更复杂的事情都可能被另一个竞争线程中断。

    即使是这么简单的事情:

    myBool = !myBool
    

    如果竞争线程修改 myBool 的值,可能会得到意想不到的结果。

    如果您想确保不会发生类似的事情,建议您使用锁。除非您确切知道自己在做什么,否则强烈建议不要使用 volatile 关键字。查看theseblogposts了解更多信息。

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

    【讨论】:

    • 强调你的榜样。在您的示例中,您确实同时读写,而 myBool = true 只是写入。因此 volatile 对于 myBool = !myBool 来说是不够的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    • 2013-09-21
    • 2010-10-24
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    相关资源
    最近更新 更多