【问题标题】:In C#, what does "atomic" mean?在 C# 中,“原子”是什么意思?
【发布时间】:2023-04-03 12:51:02
【问题描述】:

我在 C# 6.0 and the .NET 4.6 framework 一书中读到了这一点:

“赋值和简单的算术运算不是原子的”。

那么,它到底是什么意思?

【问题讨论】:

标签: c#


【解决方案1】:

原子操作是不能在中途中断的操作,例如线程。以语句为例

_value++;

如果你有两个线程同时执行这段代码,起始值为0,你可能会有以下情况

  • 线程 A 读取 _value, 0
  • 线程 A 加 1, 1
  • 线程 B 读取 _value, 0
  • 线程 B 添加 1, 1
  • 线程 A 分配给 _value, 1
  • 线程 B 分配给 _value, 1

所以现在,即使我们调用了两次增量,_value 中的最终值是1,而不是预期的2。这是因为自增运算符不是原子的。

函数Interlocked.Increment,然而,是原子的,所以将上面的代码替换为

Interlocked.Increment(ref _value);

将解决给定的竞争条件。

编辑:作为词源学的一点,“原子”通常意味着“不可分割”——我们熟悉的化学术语是一个误称原子是不可分割的,只是为了后来的发现将它们进一步分解为亚原子、夸克和量子水平。

【讨论】:

    【解决方案2】:

    原子是不可分割的。原子操作是“不可分割”的操作,不能分割,例如中断。

    微处理器不是按顺序执行的,也就是说,一条指令接一条指令,就像写在程序中一样。有外部对象,可以改变执行流程。中断就是一个很好的例子。

    所以,您可能知道MOV 指令,它几乎适用于所有处理器。
    想象一下它是由 CPU 执行的。一个 32 位的值被移动到一个 32 位的寄存器中。
    现在,在移动了 16 位之后,就会发生中断请求。

    • 原子MOV 指令不会停止,而是会执行到最后,然后 CPU 将处理中断
    • 非原子的MOV 指令将立即停止并执行中断。问题是,如果中断访问了MOV写入的寄存器,内容不清楚,因为MOV操作只完成了一半!

    现在,在普通处理器上,MOVs 在处理器字长上运行是原子的。如果处理器字为 16 位宽,则 16 位 MOV 指令将是原子的。
    但是,32 位 MOV 操作不会是原子的。这种非原子的MOV 指令通常不是由指令集提供,而是由一些高级语言提供,例如C 的long long 或C# 的long。对这些数据类型的操作不保证是原子的!

    【讨论】:

    • 有这方面的例子吗?
    • @MohamedAhmed 添加了示例。
    • @cadaniluk:许多处理器指定了不会发生中断的情况。在 80386sx 处理器(类似于 80386,但具有 16 位数据总线)上,32 位读取或写入将作为两个 16 位操作执行,但大多数类型的中断无法在它们之间提供服务。我认为在指令处理到一半时可能会发生总线故障,但是如果在读取时发生这样的中断,那么在操作的前半部分读取的值将被丢弃,并且整个读取从头重新执行。如果在写入过程中发生总线故障,...
    • ...整个写入将从头开始重新执行,因此任何完成的写入都将自动完成,但是写入操作最终会写入页面错误处理程序之前和之后的目标的前半部分。然而,大多数系统的设计使得如果在四的倍数地址上执行访问,则不会发生这样的事件序列。
    猜你喜欢
    • 2013-02-09
    • 2013-03-09
    • 2010-10-23
    • 2011-07-07
    • 2016-12-08
    • 2016-04-19
    • 2015-04-11
    • 2012-04-25
    • 2011-08-25
    相关资源
    最近更新 更多