我是嵌入式开发人员,在使用I / O端口时使用volatile关键字。 但是我的项目经理建议使用volatile关键字是有害的并且有很多缺点,但我发现大多数情况下volatile在嵌入式编程中很有用,据我所知,volatile在内核代码中是有害的,因为我们代码的更改将变为不可预知的。 嵌入式系统中使用volatile还有什么缺点吗?
不, volatile
无害。 在任何情况下。 永远。 没有可能的格式良好的代码片段会随着对象(以及指向该对象的指针)添加volatile
而中断。 然而, volatile
往往知之甚少 。 内核文档声明volatile
被认为是有害的原因是人们继续使用它来破坏内核线程之间的同步。 特别是,他们使用了volatile
变量变量,就好像它们的访问权限被保证是primefaces的一样,但事实并非如此。
volatile
也没有用,特别是如果你去裸机,你将需要它。 但是,与任何其他工具一样,在使用它之前理解volatile
的语义非常重要。
什么是volatile
的
在标准中,对volatile
对象的访问被认为是副作用 ,与递增或递减++
和--
方式相同。 特别是,这意味着5.1.2.3(3),其中说
(…)实际实现不需要评估表达式的一部分,如果它可以推断出它的值没有被使用并且没有产生所需的副作用(包括由调用函数或访问volatile对象引起的任何副作用)
不适用。 编译器必须在每个序列点丢弃它认为知道的关于volatile
变量值的所有内容。 (与其他副作用一样,当对volatile
对象的访问发生时,由序列点控制)
这种影响主要是禁止某些优化。 以代码为例
int i; void foo(void) { i = 0; while(i == 0) { // do stuff that does not touch i } }
允许编译器将其转换为永远不会再次检查i
的无限循环,因为它可以推断出i
的值在循环中没有改变,因此i == 0
将永远不会为假。 即使存在可以想象地改变i
另一个线程或中断处理程序,这也适用 。 编译器不知道它们,它并不关心。 明确允许不在乎。
与此形成鲜明对比
int volatile i; void foo(void) { i = 0; while(i == 0) { // Note: This is still broken, only a little less so. // do stuff that does not touch i } }
现在编译器必须假设i
可以随时更改并且无法进行此优化。 当然,这意味着如果处理中断处理程序和线程,则需要使用volatile
对象进行同步。 然而,它们并不足够。
什么volatile
不是
volatile
不保证是primefaces访问。 如果您习惯于嵌入式编程,这应该具有直观意义。 如果愿意,请考虑以下8位AVR MCU的代码:
uint32_t volatile i; ISR(TIMER0_OVF_vect) { ++i; } void some_function_in_the_main_loop(void) { for(;;) { do_something_with(i); // This is thoroughly broken. } }
这段代码被破坏的原因是对i
访问不是primefaces的 – 在8位MCU上不能是primefaces的。 例如,在这种简单的情况下,可能会发生以下情况:
类似的事情可能发生在PC和其他平台上。 如果有的话,更多的机会可以通过更复杂的架构打开。
带走
所以不,使用volatile
也不错,你(通常)必须在裸机代码中执行它。 但是,当你使用它时,你必须记住它不是一根魔杖,你仍然必须确保你不会绊倒自己。 在嵌入式代码中,通常采用特定于平台的方式来处理primefaces性问题; 例如,在AVR的情况下,通常的撬棍方法是在持续时间内禁用中断,如
uint32_t x; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { x = i; } do_something_with(x);
…如果事先启用了ATOMIC_BLOCK
宏,则ATOMIC_BLOCK
调用cli()
(禁用中断),然后调用sei()
(启用中断)。
C11是第一个明确承认multithreading存在的C标准,它引入了一系列新的primefaces类型和内存防护操作,可用于线程间同步,并且在许多情况下使用不必要的volatile
。 如果您可以使用它们,那么它可能需要一段时间才能到达所有常见的嵌入式工具链。 有了它们,上面的循环可以像这样修复:
atomic_int i; void foo(void) { atomic_store(&i, 0); while(atomic_load(&i) == 0) { // do stuff that does not touch i } }
……最基本的forms。 更宽松的内存顺序语义的精确语义超出了SO答案的范围,因此我将坚持使用默认的顺序一致的东西。
如果您对此感兴趣,Gil Hamilton在评论中提供了一个链接,用于解释使用C11primefaces的无锁堆栈实现,尽管我不认为这是对内存顺序语义本身的非常好的写法。 然而,C11模型似乎与C ++ 11内存模型密切相关,其中存在一个有用的表示。 如果我找到一个特定于C11的写入的链接,我会在稍后将其放在此处。
volatile
只有在这样限定的对象可以异步更改时才有用。 这种变化可能发生
在所有这些情况下,您必须声明您的对象volatile
,否则您的程序将无法正常工作。 (您可能会注意到不同线程之间共享的对象不在列表中。)
在所有其他情况下,您不应该,因为您可能缺少优化机会。 另一方面,限定不属于上述点的对象volatile
不会使您的代码不正确。
Volatile告诉编译器不要优化与volatile变量有关的任何东西。
为什么不应该使用“volatile”类型类? – 内核文档中的最佳文章
在必要和适当的情况下不使用volatile
更有可能是有害的! 对volatile
任何感知问题的解决方案不是禁止使用它,因为在许多情况下,安全和正确的语义是必要的。 相反,解决方案是了解其目的和行为。
对于可能在编译器知识之外进行更改的任何数据(例如I / O和双端口或DMA存储器),这一点至关重要。 访问执行上下文(如线程和中断处理程序)之间共享的内存也是必要的。 这也许是困惑所在; 它确保显式读取此类内存,并且不强制执行primefaces性或互斥 – 需要其他机制,但这并不排除volatile
,但它只是共享内存访问解决方案的一部分。
请参阅以下使用volatile的文章(并将它们发送给您的项目经理!):
volatile是c中的一个关键字,它告诉编译器不要对该变量进行任何类型的优化。
让我举个简单的例子:
int temp; for ( i=0 ;i <5 ; i++ ) { temp = 5; }
编译器将做什么来优化代码:
int temp; temp = 5; /* assigned temp variable before the loop. */ for ( i=0 ;i <5 ; i++ ) { }
但是如果我们提到volatile关键字,那么编译器将不会对temp变量进行任何类型的优化。
volatile int temp; for ( i=0 ;i <5 ; i++ ) { temp = 5; }
“挥发性被认为是有害的”—>我不认为挥发性有害。 如果您不希望编译器端进行任何优化,请使用volatile。
例如,考虑这段代码是由温度计公司使用的,而temp是一个变量,用于获取可随时改变的大气温度。 因此,如果我们不使用volatile,那么编译器将进行优化,并且大气温度将始终相同。
需要了解更多c/c++开发分享挥发性及其有害影响,也可以关注C/ C++技术分享栏目—计算机技术网(www.ctvol.com)!
以上就是c/c++开发分享挥发性及其有害影响相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/979345.html