我最近在这里学到以下是x86 CPU上的线程安全x86 CLR(不一定是ECMA标准CLR)
public class SometimesThreadSafe { private int value; public int Value { get { return value; } } public void Update() { Interlocked.Add(ref value, 47); } }
这是因为写入int
这样的体系结构可确保任何其他有价值的CPU缓存同步.但是在ARM CPU上,这不是线程安全的!从另一个线程读取值可以从CPU缓存中读取旧副本.
所以问题是什么CPU架构和什么版本的CLR及其变体,例如Mono,这个线程安全吗?
"线程安全"并不是适用于这种代码的正确词.对Value属性getter的访问与Update()方法完全不同步,因此您获得的值完全不可预测.包括从未见过更新.
如果Value属性getter是原子的,那么你唯一关心的就是这里.换句话说,如果您能够观察到属性的部分更新值,其中某些字节将由Update()更改而某些字节不会更改.这是CLI规范保证的.Ecma-335,第I.12.6.6节,"原子读写":
符合要求的CLI应保证当对位置的所有写访问都是原子时,对正确对齐的内存位置的读写访问不大于本机字大小(native int类型的大小)是原子的(参见§I.12.6.2).大小相同.原子写入除了写入之外不得改变任何位.除非使用显式布局控制(请参阅分区II(控制实例布局))来更改默认行为,否则应正确对齐不大于自然字大小(本机int的大小)的数据元素.对象引用应被视为存储在本机字大小中.
在C#语言规范第5.5章"变量引用的原子性"中,这种保证有所降低.它避免依赖于IntPtr的大小:
以下数据类型的读取和写入是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型.此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的.其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的.
Anyhoo,在任何架构上都不是int的问题.
如果确实是线程安全问题,那么这段代码就是完全错误的.它在任何架构上都不是线程安全的..NET内存模型中不存在"隐式易变"的概念.x86 jitter优化器利用的东西,它会将属性的支持字段存储在cpu寄存器中,而不是从内存中更新它.你永远不会看到更新.需要明确声明它是不稳定的,以抑制此优化.