我最近接受了一家软件公司的采访,他问我以下问题:
你能告诉我在变量前添加volatile的含义是什么吗?你能解释一下为什么这很重要吗?
我的大多数编程知识都来自C,但是工作岗位是针对C#的(我想我可能会根据具体问题的需要添加这些信息)
我回答说它只是让编译器知道变量可以跨进程或线程使用,并且它不应该对该变量使用优化; 优化它可以恶化行为.简而言之,它是对编译器的警告.
然而,根据采访者的说法,反过来说,volatile关键字会警告操作系统,而不是编译器.
我对此感到有些困惑,所以我做了一些研究,实际上找到了相互矛盾的答案!一些消息来源说这是针对编译器的,还有其他针对操作系统的.
这是什么?它是否因语言而异?
Eri*_*ert 23
我回答说它只是让编译器知道变量可以跨进程或线程使用,并且它不应该对该变量使用优化; 优化它可以恶化行为.简而言之,它是对编译器的警告.
这对C#来说是正确的方向,但是错过了一些重要的方面.
首先,完全删除"进程".变量不在C#中的进程之间共享.
其次,不要专注于优化.而是专注于允许的语义.编译器不需要生成最佳代码; 需要编译器来生成符合规范的代码.重新排序不一定是出于性能原因而需要更快/更小/无论如何.volatile声明对多线程程序的允许语义添加了额外的限制.
第三,不要将其视为对编译器的警告.它是编译器的一个指令:生成保证符合volatile变量规范的代码.编译器如何做到这一点取决于它.
这个问题的实际答案
你能告诉我在变量前添加volatile的含义是什么吗?
是:AC#编译器和运行时环境具有很大的自由度,可以根据他们认为合适的任何原因重新排序变量读写.它们仅限于那些在单个线程上保留程序含义的重新排序.所以"x = y; a = b;" 可以将读取的b移到读取之前; 这是合法的,因为结果没有变化.(这不是重新排序的唯一限制,但在某种意义上它是最基本的限制.)但是,重新排序允许在多个线程上注意到; 另一个线程可能会观察到在y之前读取b.这可能会导致问题.
C#编译器和运行时对可以如何相对于彼此重新排序易失性读取和写入有额外的限制,以及如何针对其他事件(例如线程启动和停止,锁定,抛出异常)对它们进行排序,等等.
有关读取,写入和其他效果的观察顺序的限制的详细列表,请参阅C#规范.
特别要注意的是,即使使用volatile变量,也不需要从所有线程看到所有变量访问的一致总排序.具体而言,挥发性"读取变量的最新值"这一概念简直就是假的; 这句话表明存在"最新价值"这一事实,这意味着总的一致性排序.
如果这听起来令人困惑,那就是.不要编写跨线程共享数据的多线程程序.如果必须,请使用最高级别的抽象.几乎没有人应该编写使用volatile的代码; 使用TPL并让它管理你的线程.
现在让我们在C的背景下考虑你的答案.
关于C,问题是不适定的.在C#中,volatile是成员变量声明的修饰符; 在C中,它是一种类型的一部分.所以说"变量之前"是模棱两可的; 变量之前的位置?a volatile int * x和an 之间有区别int * volatile x.(你能看到区别么?)
但更重要的是:C规范并不保证volatile对线程有任何特定的行为.如果您的C编译器这样做,那么这是编译器供应商对该语言的扩展.C中的易失性保证在内存映射IO,长跳转和信号方面具有某些行为,这就是全部; 如果你依赖它来获得关于线程的某些行为,那么你正在编写不可移植的代码.
根据采访者的说法:反过来说,volatile关键字会警告操作系统,而不是编译器.
从开始到结束都是胡说八道.面试官不应该问他们不理解答案的问题.
老实说,面试官提出的问题有点模糊。
这实际上取决于他/她所说的“操作系统”是什么意思。他们是在谈论“前期操作系统”(事物的纯软件方面),还是他们可能将“操作系统”误解为硬件-软件关系,即 RTE 和 MMM(我在一些文章中看到了假设和比较)我自己的个人采访)。我认为应该指出的是,这两者是截然不同的!如果他/她正在谈论前者,那么NO易失性不会“通知”操作系统。如果他们谈论的是后者,那么是的(这是一个松散的“是”)。此时,您正处于语言之间差异的领域。正如 Cody Gray 提到的,C# 是一种托管语言,因此操作系统的后一个定义确实会“收到”变量和要采取的预防措施的通知。
而且,在任何情况下或操作系统的定义中,编译器都会专门管理和处理易失性字段,无论语言如何。否则,为什么要把关键字放在第一位呢?
在我个人看来,无论它的价值是什么,我认为你的回答是正确的,尽管从评论来看,本质上可能会变得复杂和忙碌。
| 归档时间: |
|
| 查看次数: |
557 次 |
| 最近记录: |