繁体   English   中英

我们真的需要对全局 int 变量使用 Interlocked 吗?

[英]Do we really need to use Interlocked for global int variables?

我的问题基于这篇文章https://www.harshmaurya.in/volatile-vs-lock-vs-interlocked-in-c-net/其中显示了一个示例:

private int _counter = 0;

并假设您有多个线程执行_counter++;

文章建议使用 Interlocked class 像Interlocked.Increment(ref _counter);

我可以理解为什么需要 Interlocked 来确保将值写入变量是原子操作,因为单行 C# 代码在处理器中是三个步骤(三行 CPU 指令),所以如果写操作不是原子的,每个线程可以interleave,它错误地覆盖了最终结果。

但我也通过 C# 阅读了 CLR 一书,其中说:

CLR 保证对以下数据类型的变量的读写是原子的:Boolean、Char、(S)Byte、(U)Int16、(U)Int32、(U)IntPtr、Single 和引用类型。 这意味着该变量中的所有字节都被一次读取或写入。

我们知道int实际上是Int32 ,既然 CLR 已经确保对int的操作是原子的,那么为什么我们需要 Interlocked 来确保它们再次是原子的呢?

写入是原子的这一事实仅意味着您在更新期间永远看不到语义上从未存在过的第三个值,即旧值的一部分(一些字节)和新值的一部分(一些字节)-a撕裂的价值。 这是要避免的重要事情,但与Interlocked.Increment试图解决的问题不同; 而是试图防止丢失更新,即使用两个线程:

  • (线程 A 和 B 都想递增)
  • 线程 A 将值 3 读入本地堆栈
  • 线程 B 将值 3 读入本地堆栈
  • 线程 A 增加局部值得到 4
  • 线程 B 增加局部值得到 4
  • 线程 A 将局部值写入全局,将其保留为 4
  • 线程 B 将局部值写入全局,将其保留为 4

在这里,两个线程都认为他们从 3 开始增加值,但我们最终只得到 4,而不是我们期望从两个增量中得到的 5。 这是因为整个操作现在被视为原子操作(如何发生取决于实现;它可能是 CPU 固有的,或者如果不可用,则可能是比较交换循环)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM