C#bool是原子的,为什么volatile是有效的

时间:2016-07-03 01:47:01

标签: c# multithreading boolean atomic volatile

C#中,我们知道bool是原子的 - 那么为什么将其标记为volatile是有效的?什么是差异,什么是一个好的(甚至是实际的)用例与另一个?

bool _isPending;

对战

volatile bool _isPending; // Is this realistic, or insanity?

我做了一些阅读herehere,我试图确保我完全理解两者的内部运作。我想了解何时使用一个与另一个相对应,或者仅bool就足够了。

2 个答案:

答案 0 :(得分:11)

  

在C#中,我们知道bool是原子的 - 那么为什么将它标记为volatile是有效的呢?什么是差异,什么是一个好的(甚至是实际的)用例与另一个?

您的问题的假设是您认为volatile使访问成为原子。但是波动性和原子性是完全不同的东西,所以不要把它们混为一谈。

Volatility是编译器和运行时被限制进行某些优化的属性,这些优化涉及相对于彼此向前和向后移动变量的读取和写入,更一般地说,相对于其他重要事件,如启动和停止线程,运行构造函数等。有关可见副作用可能会或可能不会重新排序操作的详细列表,请参阅C#规范。

原子性是特定操作只能被观察为未开始或完全完成,并且从未“中途完成”的属性。

从定义中可以看出,这两件事情无论彼此都没有。

在C#中,所有对引用,bools和大小为4或更小的整数类型的访问都保证是原子的。

现在,在C#中,原子性和波动性之间存在一些轻微的非正交性,因为只有原子类型的字段可能被标记为易失性。例如,你可能不会制造挥发性的双倍。说“我们将限制读写如何优化但仍允许撕裂”将是非常奇怪和危险的。由于波动性不会导致原子性,因此您不希望让用户认为操作是原子的,因为它也是不稳定的。

你应该阅读我的一系列文章,这些文章更详细地解释了这些事物之间的差异,以及它们实际上的变化,以及为什么你几乎不能理解安全地使用它。

https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are-different-part-one/

https://ericlippert.com/2011/05/31/atomicity-volatility-and-immutability-are-different-part-two/

https://ericlippert.com/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three/

http://blog.coverity.com/2014/03/12/can-skip-lock-reading-integer/

如果您认为在阅读完所有内容后了解波动性,我邀请您尝试解决我在这里提出的难题:

https://web.archive.org/web/20160729162225/http://blog.coverity.com/2014/03/26/reordering-optimizations/

答案 1 :(得分:2)

如果前一个或后一个代码中的变量有更新,并且更新发生的顺序很关键,那么将该字段标记为volatile将确保在任何先前的更新之后对该字段进行更新在任何后续更新之前。

换句话说,如果_isPendingvolatile,那么编译器不会导致这些指令以不同的顺序执行:

_someVariable = 10;
_isPending = true;
_someOtherVariable = 5;

无论是否是多线程,如果我们根据相邻行中的这些更新是否以指定的顺序发生而编写的代码中断,那么就会出现问题。我们应该问为什么这个序列很重要。 (如果存在重要的情况,请设想尝试在注释中解释它,以便没有人对代码进行重大更改。)

对于几乎所有阅读上述代码的人来说,这些操作的顺序似乎根本不重要。如果它们确实重要,则意味着读取我们代码的其他人无法理解正在发生的事情。他们可以做一些重构,重新排序这些代码行,并在不知情的情况下破坏所有内容。它甚至可能在测试时发挥作用,然后在部署时无法预测且不一致。

我同意你所链接的答案中的Eric Lippert's comment

  坦率地说,我不鼓励你做一个不稳定的领域。挥发物   字段表明你正在做一些彻头彻尾的疯狂:你是   尝试在两个不同的线程上读取和写入相同的值   没有锁定。

我想我没有直接回答方向。 volatile对类型(包括bool)有效,因为可以对该类型执行原子操作。 volatile可以防止编译器优化。根据{{​​1}}的文档,

  

这可确保在字段中显示最新值   所有时间。

但是如果该字段不能以32位或更少的比特表示,那么无论如何都不能保证编译器优化。