3

快速提问。低于原子分配:

object [] table = new object[10]
...
table[3] = 10; //is it atomic ?
...
4

3 回答 3

5

是的,赋值是原子的,因为 object 是引用类型。

正如下面从 MSDN 引用中提到的,对于大多数(不是全部)内置值类型(如下所述)和引用类型来说,它们都是原子的。CLI 保证对处理器自然指针大小(或更小)的值类型变量的读取和写入是原子的;如果您在 64 位版本的 CLR 中的 64 位操作系统上运行 C# 代码,那么 64 位双精度和长整数的读写也保证是原子的。C# 语言不保证这一点,但运行时规范可以。

MSDN 说什么

以下数据类型的读写是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。此外,上一个列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读取和写入,包括 long、ulong、double 和 decimal,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。

在这里可以看到埃里克本人的非常详细的回答

于 2013-08-23T06:35:37.780 回答
3

在谈论原子性时,我假设 OP 意味着“线程安全”。

仅当另一个线程可能读取部分写入的值时,写入操作才不是原子的。

如果object [] table是本地方法,每个线程将获得自己的表,因此对表的任何操作都是原子的。

展望未来,我假设它table是跨线程共享的。

OP 已将 table 定义为object. 因此table[3] = 10涉及拳击。

即使table[3] = 10代表一条指令链,此操作也是原子操作,因为这最终将写入装箱实例的“地址”(当前的 CLR 实现确实使用内存地址表示对象引用)并且地址是机器的自然字长(即 32 位机器上的 32 位和 64 位机器上的 64 位)。请注意,尽管上述解释是基于当前的 CLR 实现,但规范保证了参考编写的原子性。装箱操作和装箱实例本身是线程本地的,因此其他线程无法干预。即使值超过字长(例如Decimal) 正在编写,操作将是原子的(由于装箱)。这里必须注意,只有当正在写入的值已经以线程安全的方式获得时,上述参数才成立。

如果不涉及装箱,那么通常的字长写入规则(或由于内存对齐而小于字长)是原子的。

于 2013-08-23T08:15:30.067 回答
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 处加载,然后在 IL_000D 处对 int 进行装箱,最后将装箱后的值存储在 IL_0012 处。

这些指令中的每一个都可能是原子的,但这并不意味着生成的指令链就是原子的table[3] = 10;

于 2013-08-23T06:54:43.840 回答