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

本文关键字:有效 volatile bool 原子的 为什么 | 更新日期: 2023-09-27 17:54:03

c# 中,我们知道bool是原子的-那么为什么将其标记为volatile是有效的?它们之间的区别是什么,什么是好的(甚至是实用的)用例?

bool _isPending;

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

我在这里和这里做了一些阅读,我试图确保我完全理解这两者的内部工作原理。我想知道什么时候使用一个比另一个合适,或者仅仅使用bool就足够了。

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

在c#中,我们知道bool是原子型的,那么为什么将其标记为volatile是有效的呢?它们之间的区别是什么,什么是好的(甚至是实用的)用例?

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

波动性是一种属性,编译器和运行时受到限制,无法进行某些优化,这些优化涉及相对于彼此向前或向后移动变量的读和写,更一般地说,涉及其他重要事件,如启动和停止线程,运行构造函数等。参考c#规范的详细列表,了解操作可以或不可以根据可见的副作用重新排序。

原子性是这样一种属性:一个特定的操作只能在未开始或完全完成时被观察到,而不能在"完成了一半"时被观察到。

从定义中可以看出,这两件事之间没有任何关系。

在c#中,所有对引用、bool和小于等于4的整数类型的访问都保证是原子的。

现在,在c#中原子性和易失性之间有一些轻微的非正交性,在中只有原子类型的字段可以标记为易失性。例如,不能使用易失双精度类型。如果我们说"我们将限制读取和写入的优化方式,但仍然允许撕裂",那将是非常奇怪和危险的。由于volatile并不会导致原子性,因此您不希望用户仅仅因为操作也是volatile而认为它是原子性的。

您应该阅读我的系列文章,这些文章更详细地解释了这些东西之间的区别,以及volatile的实际作用,以及为什么您对它的了解还不足以安全使用它。

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/

https://web.archive.org/web/20160323025740/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/

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

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

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

无论是否多线程,如果我们编写的代码会根据相邻行的这些更新是否按照指定的顺序发生而中断,那么就有问题了。我们应该问为什么这个顺序很重要。(如果在某个场景中这很重要,想象一下在注释中解释它,这样就不会有人对代码进行破坏性的更改。)

对于几乎所有阅读上面代码的人来说,这些操作的顺序似乎根本无关紧要。如果它们很重要,那就意味着其他阅读我们代码的人不可能理解发生了什么。他们可以做一些重构,重新排序那些代码行,并在不知情的情况下破坏一切。它甚至可能在测试时工作,然后在部署时不可预测地和不一致地失败。

我同意Eric Lippert在你链接的答案中的评论:

坦率地说,我不鼓励您创建volatile字段。挥发性字段表示你正在做一些疯狂的事情:你是试图在两个不同的线程上读写相同的值不加锁


我想我没有直接回答方向。volatile对于类型(包括bool)是有效的,因为可以对该类型执行原子操作。volatile保护编译器不进行优化。根据volatile的文档,

这确保最新的值出现在字段at中。

但是如果该字段不能用32位或更少表示,那么阻止编译器优化无论如何也不能保证。