在C#中哪些操作是原子操作?

63

是否有一种系统性的方法能够知道C#中的操作是否是原子操作?或者有任何一般性的指南或经验法则吗?

3个回答

48

如果需要更完整/详细的信息:

读写32位值类型是原子性的:这包括以下内在值(struct)类型:bool, char, byte, sbyte, short, ushort, int, uint, float。以下类型(以及其他类型)不保证是原子性的:decimal, double, long, ulong

例如:

int x;
x = 10; // atomic
decimal d;

d = 10m; // not atomic

引用赋值也是一个原子操作:

private String _text;
public void Method(String text)
{
  _text = text; // atomic
}

24
如果你的系统本地字长为64位(例如,运行64位Windows操作系统的x64处理器),64位值(long)也是原子性的,这个说法并不严格正确。请注意,这里的翻译并没有改变原文的意思,只是将其转化为通俗易懂的语言。 - Chris Shain
1
64位应用程序中的引用是否是原子性的? - Eric
1
从C#规范中可以看出,对于其他类型的读写操作,包括long、ulong、double和decimal,以及用户定义的类型,不能保证是原子性的。@ChrisShain - Peter Ritchie
1
@PeterRitchie C#规范指出long类型不保证原子性,但这并不意味着禁止原子性。我的理解是,在64位CLI上,由于我在答案中发布的CLI规范部分的保证,它们是原子性的。话虽如此,如果该领域的权威人士能够说明64位平台上访问long类型为什么是非原子性的,我愿意被证明是错误的。 - Chris Shain
3
@ChrisShain,你是正确的,规范并没有说它们不是原子操作或者永远不会是原子操作,我已经澄清了我的回答(本意并不是暗示它们永远不会是原子操作,只是不能保证)。但是,你必须编译为x64才能确保它是原子的。如果你没有为x64编译,代码可能在x86上运行,因此不是原子的;所以你必须假设它不是原子的,并使用lock或Monitor.Enter/Exit来确保它被原子地访问。 - Peter Ritchie
显示剩余7条评论

35
是的。请查阅CLI规范:http://www.ecma-international.org/publications/standards/Ecma-335.htm。例如: I.12.6.6 原子读写 一个符合规范的CLI应该保证对于大小不超过本地字长(即本地int类型大小)的正确对齐内存位置的读写访问是原子的(详见§I.12.6.2),当所有对一个位置的写入访问都是相同大小的时候。原子写入不应更改除了被写入的位之外的任何位。除非使用显式布局控制(参见第II部分(控制实例布局))来更改默认行为,否则不大于自然字长(本地int大小)的数据元素应正确对齐。对象引用应被视为存储在本地字长中。 [注:除了作为类库的一部分提供的用于此目的的方法之外,没有关于内存原子更新(读取-修改-写入)的保证(请参阅第IV部分)。当硬件不支持直接写入小数据项时,在“小数据项”(大小不超过本地字长的项)上进行原子写入是必需的。结束语] [注:当本地int大小为32位时,即使某些实现在8字节边界上对齐数据时执行原子操作,也不能保证对8字节数据的原子访问。结束语]

关于64位长整型问题,Eric Lippert在这里回答了它: https://ericlippert.com/2011/05/31/atomicity-volatility-and-immutability-are-different-part-two/

CLI规范实际上提供了更强的保证。CLI保证对于值类型变量的读写是原子性的,如果这些变量的大小(或更小)等于处理器的自然指针大小;如果在64位版本的CLR中运行C#代码,则64位双精度和长整型的读写也保证是原子性的。C#语言并不保证这一点,但运行时规范确实保证了。 (如果在某些没有由CLI的某些实现实现的环境中运行C#代码,则当然不能依赖该保证;如果想知道他们提供的保证,请联系售卖给你运行时的供应商。)
关于原子访问的另一个微妙之处在于,仅当正在读取或写入的变量与存储相关联且对齐到内存中正确位置时,底层处理器才保证原子性。最终,变量将被实现为指向某个位置的内存指针。在32位操作系统上,该指针必须能够被4整除,以保证读取或写入是原子性的,在64位操作系统上,它必须能够被8整除。

Eric说:“就64位值和原子性而言,C#语言并不保证这一点。”只有在特定的CLI下才可能实现。虽然这有点学究气,但OP没有指定是哪个CLI... - Peter Ritchie

8
您可以在此处获取 CLI 规范:链接。规范中写到:“符合 CLI 的实现必须保证,对于原生字长(即类型 native int)大小的内存位置进行读写访问时,其操作是原子的……”。C# 规范中的第 12.5 节可在此处找到,其中写道:“以下数据类型的读写操作是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。” 另外还提到:“不能保证具有原子化的读改写操作,例如增量或减量操作。” 您可以使用此工具使增量操作变成原子操作。

1
除了 Interlocked 类,它确实具有原子增量、减量和其他几个功能。 - David Yaw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接