易变变量
Volatile variables
我最近接受了一家软件公司的面试,他问了我以下问题:
Can you describe to me what adding volatile in front of variables does? Can you explain to me why it's important?
我的大部分编程知识都来自 C,但工作职位是 C#(我想如果有必要,我可能会添加这些信息来专门解决这个问题)
我回答说它只是让 编译器 知道变量可以跨进程或线程使用,并且它应该不对该变量使用优化;因为优化它会恶化行为。简而言之,这是对编译器的警告。
然而据面试官说,情况恰恰相反,volatile关键字警告OS,不是编译器。
我对此有点困惑,所以我做了一些研究,实际上发现了相互矛盾的答案!一些消息来源说它是针对编译器的,而其他消息来源说是针对 OS.
这是哪个?它因语言而异吗?
老实说,面试官提的问题有点模糊。
这实际上取决于 he/she 和 "OS" 的含义。他们是在谈论 "upfront OS",事物的纯软件方面,还是他们可能将 "OS" 误解为硬件-软件关系,即 RTE 和 MMM(我已经看到假设和在我自己的一些个人访谈中进行比较)。我认为应该指出的是,这两者是截然不同的!如果he/she说的是前者,那么NOvolatile不会"inform"OS。如果他们谈论的是后者,那么 yes(这是一个松散的是)。在这一点上,您处于语言之间差异的领域。正如 Cody Gray 提到的,C# 是一种托管语言,因此 OS 的后一个定义确实 "get notified" 变量和要采取的预防措施。
而且,在 OS 的任何情况下或定义中,编译器 确实 专门管理和处理易失性领域,与语言无关。不然怎么把关键词放在第一位?
在我个人看来,不管那是什么价值,我认为你回答正确,尽管从评论来看,本质上可能会变得复杂和忙碌。
I answered by saying it just lets the compiler know that the variable can be used across processes or threads, and that it should not use optimization on that variable; as optimizing it can deteriorate behavior. In a nutshell, it's a warning to the compiler.
这是 C# 的正确方向,但遗漏了一些重要方面。
首先,完全删除 "processes"。在 C# 中不跨进程共享变量。
其次,不要专注于优化。而是专注于 允许的语义 。不需要编译器来生成最佳代码;需要编译器来生成符合规范的代码。重新排序不一定是出于性能原因,也不需要更快/更小/其他。 volatile 声明对多线程程序的允许语义添加了额外的限制。
第三,不要认为这是对编译器的警告。它是对编译器的指令:生成保证符合易失性变量规范的代码。编译器如何做到这一点取决于它。
问题的实际答案
Can you describe to me what adding volatile in front of variables does?
是:C# 编译器和运行时环境有很大的自由度,可以出于他们认为合适的任何原因重新排序变量读写。它们仅限于那些在单个线程上保留程序含义的重新排序。所以 "x = y; a = b;" 可以将 b 的读取移动到 y 的读取之前;这是合法的,因为结果没有改变。 (这不是对重新排序的唯一限制,但从某种意义上说,它是最基本的限制。)但是,允许在多个线程上注意到重新排序;另一个线程可能观察到 b 在 y 之前被读取。这可能会导致问题。
C# 编译器和运行时对可变读取和写入如何相对于彼此重新排序以及它们如何相对于其他事件(如线程启动和停止、锁定)进行排序有额外的限制,抛出异常,等等。
有关读取、写入和其他效果的观察顺序限制的详细列表,请参阅 C# 规范。
请特别注意,即使使用易失性变量,也不需要从所有线程看到的所有变量访问的总排序一致。具体来说,volatile "reads the latest value of the variable" 的概念是错误的;该措辞表明存在 "the latest value" 这样的东西,这意味着完全一致的排序。
如果这听起来令人困惑,那确实是。 不要编写跨线程共享数据的多线程程序。 如果必须,请使用最高级别的抽象。几乎没有人应该编写使用 volatile 的代码;使用 TPL 并让它管理您的线程。
现在让我们在 C 的上下文中考虑你的答案。
关于 C 的问题是不适定的。在 C# 中,volatile 是成员变量声明的修饰符;在 C 中,它是 type 的一部分。所以说 "before a variable" 是模棱两可的;变量之前在哪里? volatile int * x
和 int * volatile x
是有区别的。 (你能看出区别吗?)
但更重要的是:C 规范不保证 volatile 对线程有任何特定的行为。如果您的 C 编译器支持,那是您的编译器供应商对语言的扩展。 C 中的 volatile 保证在内存映射 IO、长跳转和信号方面具有某些行为,仅此而已;如果您依赖它对线程具有某些行为,那么您正在编写不可移植的代码。
According to the interviewer: it's the other way around, and the volatile keyword warns the OS, not the compiler.
从头到尾都是废话。面试官不应该问他们不明白答案的问题。
我最近接受了一家软件公司的面试,他问了我以下问题:
Can you describe to me what adding volatile in front of variables does? Can you explain to me why it's important?
我的大部分编程知识都来自 C,但工作职位是 C#(我想如果有必要,我可能会添加这些信息来专门解决这个问题)
我回答说它只是让 编译器 知道变量可以跨进程或线程使用,并且它应该不对该变量使用优化;因为优化它会恶化行为。简而言之,这是对编译器的警告。
然而据面试官说,情况恰恰相反,volatile关键字警告OS,不是编译器。
我对此有点困惑,所以我做了一些研究,实际上发现了相互矛盾的答案!一些消息来源说它是针对编译器的,而其他消息来源说是针对 OS.
这是哪个?它因语言而异吗?
老实说,面试官提的问题有点模糊。
这实际上取决于 he/she 和 "OS" 的含义。他们是在谈论 "upfront OS",事物的纯软件方面,还是他们可能将 "OS" 误解为硬件-软件关系,即 RTE 和 MMM(我已经看到假设和在我自己的一些个人访谈中进行比较)。我认为应该指出的是,这两者是截然不同的!如果he/she说的是前者,那么NOvolatile不会"inform"OS。如果他们谈论的是后者,那么 yes(这是一个松散的是)。在这一点上,您处于语言之间差异的领域。正如 Cody Gray 提到的,C# 是一种托管语言,因此 OS 的后一个定义确实 "get notified" 变量和要采取的预防措施。
而且,在 OS 的任何情况下或定义中,编译器 确实 专门管理和处理易失性领域,与语言无关。不然怎么把关键词放在第一位?
在我个人看来,不管那是什么价值,我认为你回答正确,尽管从评论来看,本质上可能会变得复杂和忙碌。
I answered by saying it just lets the compiler know that the variable can be used across processes or threads, and that it should not use optimization on that variable; as optimizing it can deteriorate behavior. In a nutshell, it's a warning to the compiler.
这是 C# 的正确方向,但遗漏了一些重要方面。
首先,完全删除 "processes"。在 C# 中不跨进程共享变量。
其次,不要专注于优化。而是专注于 允许的语义 。不需要编译器来生成最佳代码;需要编译器来生成符合规范的代码。重新排序不一定是出于性能原因,也不需要更快/更小/其他。 volatile 声明对多线程程序的允许语义添加了额外的限制。
第三,不要认为这是对编译器的警告。它是对编译器的指令:生成保证符合易失性变量规范的代码。编译器如何做到这一点取决于它。
问题的实际答案
Can you describe to me what adding volatile in front of variables does?
是:C# 编译器和运行时环境有很大的自由度,可以出于他们认为合适的任何原因重新排序变量读写。它们仅限于那些在单个线程上保留程序含义的重新排序。所以 "x = y; a = b;" 可以将 b 的读取移动到 y 的读取之前;这是合法的,因为结果没有改变。 (这不是对重新排序的唯一限制,但从某种意义上说,它是最基本的限制。)但是,允许在多个线程上注意到重新排序;另一个线程可能观察到 b 在 y 之前被读取。这可能会导致问题。
C# 编译器和运行时对可变读取和写入如何相对于彼此重新排序以及它们如何相对于其他事件(如线程启动和停止、锁定)进行排序有额外的限制,抛出异常,等等。
有关读取、写入和其他效果的观察顺序限制的详细列表,请参阅 C# 规范。
请特别注意,即使使用易失性变量,也不需要从所有线程看到的所有变量访问的总排序一致。具体来说,volatile "reads the latest value of the variable" 的概念是错误的;该措辞表明存在 "the latest value" 这样的东西,这意味着完全一致的排序。
如果这听起来令人困惑,那确实是。 不要编写跨线程共享数据的多线程程序。 如果必须,请使用最高级别的抽象。几乎没有人应该编写使用 volatile 的代码;使用 TPL 并让它管理您的线程。
现在让我们在 C 的上下文中考虑你的答案。
关于 C 的问题是不适定的。在 C# 中,volatile 是成员变量声明的修饰符;在 C 中,它是 type 的一部分。所以说 "before a variable" 是模棱两可的;变量之前在哪里? volatile int * x
和 int * volatile x
是有区别的。 (你能看出区别吗?)
但更重要的是:C 规范不保证 volatile 对线程有任何特定的行为。如果您的 C 编译器支持,那是您的编译器供应商对语言的扩展。 C 中的 volatile 保证在内存映射 IO、长跳转和信号方面具有某些行为,仅此而已;如果您依赖它对线程具有某些行为,那么您正在编写不可移植的代码。
According to the interviewer: it's the other way around, and the volatile keyword warns the OS, not the compiler.
从头到尾都是废话。面试官不应该问他们不明白答案的问题。