c/c++语言开发共享挥发性及其有害影响

我是嵌入式开发人员,在使用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

      (0)
      上一篇 2021年12月12日
      下一篇 2021年12月12日

      精彩推荐