代码之家  ›  专栏  ›  技术社区  ›  dan-gph

读取和写入属性是C中的原子属性吗?

  •  19
  • dan-gph  · 技术社区  · 15 年前

    读写C中的某些基元类型,例如 bool int 是原子的。

    (见C语言规范第5.5节“变量引用的原子性”。

    但是如何通过属性访问这些变量呢?假设它们也是原子的和线程安全的,这是合理的吗?例如,是指 MyProperty 低于原子和线程安全?:

    public bool MyProperty { get { return _foo; } }
    

    那么自动实现的属性呢?

    public bool MyProperty { get; }
    
    6 回复  |  直到 15 年前
        1
  •  29
  •   Jon Skeet    15 年前

    您需要更密切地区分“原子”和“线程安全”。正如您所说,对于大多数内置值类型和引用,写操作是原子的。

    但是,这并不意味着它们是线程安全的。它只是意味着,如果同时写入值“a”和“b”,线程将永远看不到介于两者之间的内容。(例如,从1到4的更改永远不会显示5、2或1或4以外的任何值。) 意味着一个线程一旦写入变量,就会看到值“b”。为此,您需要从波动性的角度来看内存模型。如果没有内存屏障(通常通过锁定和/或易失性变量获得),对主内存的写入可能会延迟,并且读取可能会提前,有效地假设值自上次读取后没有改变。

    如果你有一个计数器,你要它的最新值,但从来没有 收到 最新的值是由于缺少内存屏障,所以我认为您不能合理地称该线程安全,即使每个操作可能都是原子操作。

    这与属性无关,但是-属性只是简单的方法,周围有句法糖分。它们对线程没有额外的保证。.NET 2.0内存模型 比ECMA模型有更多的保证,并且有可能在方法入口和出口周围做出保证。这些保证也应该适用于属性,尽管我会对这些规则的解释感到紧张:有时很难对内存模型进行推理。

        2
  •  2
  •   JaredPar    15 年前

    我有点不清楚你在问什么。好像你可以问两个问题中的一个

    1. 是读 _foo 调用MyProperty Atomic时?
    2. MyProperty的返回值是原子设置的吗?

    对于1,答案是“是”。由于c语言规范(和cli)的状态,某些指定类型的变量的读和写都保证是原子的。“bool”类型就是这些类型之一。

    至于2,最好看一下CLI规范的第12.6.6节。它指出

    一致的CLI应保证对不大于本机字大小(native int类型的大小)的正确对齐的内存位置的读写访问是原子的。

    考虑到要使用MyProperty的返回值,您必须读取或写入该值,可以安全地假定它是原子设置的。

        3
  •  1
  •   Jon    15 年前

    原子性仅仅意味着如果有多个线程正在向一个变量写入数据,那么它的值就不会被破坏(例如,如果它占用两个字节,那么就永远不会得到线程1的高字节和线程2的低字节)。它确实 意味着变量是线程安全的。您仍然可以得到一个线程的常见线程问题,即看不到其他线程的变化等。

    为了线程安全,您需要使用锁定(或易失性,但这很难解释)。属性没有什么特别的-它们也需要显式地成为线程安全的。

        4
  •  1
  •   Richard Szalay    15 年前

    如果检查自动生成的代码,您将看到自动生成的属性 不是 线程安全-它们是简单的获取/设置到生成的字段。事实上,这样做会对性能造成很大影响(尤其是在不需要的时候)。

    此外,如果计划从多个线程访问int/bool值,则应将其(字段)标记为 volatile . 这基本上可以防止与CPU寄存器相关的多线程问题。(这是在 附加 锁定,而不是另一种选择)

        5
  •  0
  •   Jonathan Schuster    15 年前

    我想不会。属性本质上只是一些带有语法糖分的方法,使它们更容易使用。因此,在默认情况下,它们不比普通方法调用(也就是说,根本不安全线程)更安全。

        6
  •  0
  •   Larry Watanabe    15 年前

    你可以把任何东西放在一个地方,例如

        Public Property foo() As Boolean
            Get
               goToLunch()
               barbecueRibs()
               return m_foo
            End Get
            Set(ByVal value As Boolean)
               takeANap()
               accessDatabase()
               messUpOtherVariables()
               m_foo = value
            End Set
        End Property