快速提问。低于赋值原子:
object [] table = new object[10]
...
table[3] = 10; //is it atomic ?
...
答案 0 :(得分:4)
是的,赋值是原子的,因为object是引用类型。
如下面引用的那样,MSDN写入对大多数(不是全部)内置值类型(如下所述)和引用类型都是原子的。 CLI保证读取和写入处理器的自然指针大小的大小(或更小)的值类型的变量是原子的;如果您在64位版本的CLR中在64位操作系统上运行C#代码,则64位双精度和长整数的读写也保证是原子的。 C#语言不保证,但运行时规范确实如此。
MSDN说什么
以下数据类型的读写是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型。此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。除了为此目的而设计的库函数之外,不保证原子读 - 修改 - 写,例如在递增或递减的情况下。
可以看到埃里克本人非常详细的回答here
答案 1 :(得分:3)
我假设OP意味着线程安全'在谈论原子性时。
如果另一个线程可能读取部分写入的值,则写操作不仅仅是原子操作。
如果object [] table
是方法的本地,每个线程将获得自己的表,因此对表的任何操作都将是原子的。
继续我假设table
在线程之间共享。
OP已将表定义为object
的数组。因此table[3] = 10
涉及拳击。
即使table[3] = 10
代表一串指令,这个操作也是原子的,因为这最终会写出地址'盒装实例(当前CLR实现确实表示使用存储器地址的对象引用)和地址是机器的自然字大小(即32位机器上的32位和64位机器上的64位)。请注意,尽管上述说明基于当前的CLR实现,但参考编写的原子性由规范保证。装箱操作和盒装实例本身是线程的本地操作,因此其他线程无法干扰它。
即使在写入超过单词大小的值(例如Decimal
)时,也会采用相同的推理,操作将是原子的(由于装箱)。
这里必须注意的是,只有在写入的值已经以线程安全的方式获得时,上述参数才成立。
如果没有涉及拳击,那么通常的字大小规则写入(或者由于内存对齐而小于字大小的大小)是原子保持。
答案 2 :(得分:2)
我严重怀疑整个术语table[3] = 10;
是原子的。虽然对某些数据类型的单个读取或写入是原子数组访问很可能不是。
您的示例的IL代码是
IL_0001: ldc.i4.s 0A
IL_0003: newarr System.Object
IL_0008: stloc.0 // table
IL_0009: ldloc.0 // table
IL_000A: ldc.i4.3
IL_000B: ldc.i4.s 0A
IL_000D: box System.Int32
IL_0012: stelem.ref
你可以看到索引是在IL_000A加载的,然后int的装箱发生在IL_000D,最后装箱的值存储在IL_0012。
这些指令的每个不一致都可能是原子的,但这并不意味着使table[3] = 10;
的指令链成为。