对C#中某些基本类型的读写操作,例如bool
和int
是原子的。
(参见第5.5节“5.5变量引用的原子性”,在C#语言规范中。)
但是通过属性访问这些变量呢?假设它们也是原子和线程安全的是否合理?例如。读取MyProperty
低于原子和线程安全吗?:
public bool MyProperty { get { return _foo; } }
那么自动实现的属性呢?
public bool MyProperty { get; }
答案 0 :(得分:29)
您需要更密切地区分“原子”和“线程安全”。正如您所说,对于大多数内置值类型和引用,写入都是原子的。
然而,这并不意味着它们是线程安全的。它只是意味着如果写入“A”和“B”值,线程将永远不会看到它们之间的某些东西。 (例如,从1到4的变化永远不会显示5或2,或者除1或4之外的任何值。)不意味着一个线程将尽快看到值“B”它被写入变量。为此,您需要根据波动率来查看内存模型。如果没有通常通过锁定和/或易失性变量获得的内存屏障,可能会延迟对主存储器的写入,并且可能会提前读取,实际上假设自上次读取后该值未发生更改。
如果你有一个计数器并且你问它的最新价值但是由于缺乏内存障碍而从未收到最新值,我认为你不能合理地称之为线程安全即使每个操作都可能是原子的。
这与属性无关,但是 - 属性只是围绕它们的语法糖的方法。它们不会对线程提供额外的保证。 .NET 2.0内存模型确实比ECMA模型具有更多的保证,并且它可以保证方法进入和退出。这些保证也应适用于物业,虽然我对这些规则的解释感到紧张:有时候很难推断出记忆模型。
答案 1 :(得分:2)
我有点不清楚你在这里问的是什么。看起来你可能会问2个问题中的一个
_foo
原子?对于#1,答案是肯定的。正如C#语言规范(和CLI)所述,某些指定类型的变量的读写保证是原子的。 “bool”类型就是其中之一。
至于#2,最好看的地方是CLI规范的第12.6.6节。它说明了
符合标准的CLI应保证对正确对齐的内存位置的读写访问权限不大于本机字大小(native int类型的大小)是原子的
考虑使用MyProperty的返回值,您必须要么正在读取或写入值,所以假设它是原子设置是安全的。
答案 2 :(得分:1)
如果检查自动生成的代码,您将看到自动生成的属性 NOT 线程安全 - 它们很容易获取/设置为生成的字段。事实上,这样做会造成太大的性能损失(特别是在不需要时)。
此外,如果您计划从多个线程访问int / bool值,则应将其(字段)标记为volatile。这基本上可以防止与CPU寄存器相关的多线程问题。 (这是添加到锁定,而不是替代方案)
答案 3 :(得分:1)
Atomicity只是意味着如果多个线程正在写入变量,其值将不会被破坏(例如,如果它占用两个字节,则永远不会获得线程1的高字节和线程2的低字节)。 不意味着该变量是线程安全的。您仍然可以获得一个线程的常见线程问题,而不会看到其他更改等。
对于线程安全,您需要使用锁定(或易失性,但这很难推理)。属性没有什么特别之处 - 它们也需要明确地用于线程安全。
答案 4 :(得分:0)
我想不会。属性本质上只是在它们上面带有一点语法糖的方法,以使它们更容易使用。因此,默认情况下,它们不比普通方法调用更具线程安全性(也就是说,根本不是线程安全的)。
答案 5 :(得分:0)
您可以在属性中放置任何内容,例如
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