ISR和多线程程序中的C'Volatile'关键字?

use*_*625 8 c embedded multithreading

我读到了volatile内存映射硬件寄存器,ISR和多线程程序中C 关键字的用法.

1)注册

uint8_t volatile * pReg;
while (*pReg == 0) { // do sth } // pReg point to status register 
Run Code Online (Sandbox Code Playgroud)

2)ISR

int volatile flag = 0;
int main() 
{
    while(!flag) { // do sth }
}
interrupt void rx_isr(void)
{
    //change flag
}
Run Code Online (Sandbox Code Playgroud)

3)多线程

int volatile var = 0;
int task1()
{
    while (var == 0) { // do sth }
}
int task2()
{
    var++;
}
Run Code Online (Sandbox Code Playgroud)

我可以看出为什么编译器可以错误地优化while以防万一1)如果volatile不存在,'因为变量是由硬件进行的,编译器可能看不到代码所做的变量的任何变化.

但对于案例2)和3),为什么需要挥发性?在这两种情况下,变量都声明为全局变量,编译器可以看到它在多个地方使用.那么为什么编译器会优化while循环,如果变量不是volatile

是因为编译器副设计不知道"异步调用"(在ISR情况下)或多线程?但这不可能,对吧?

此外,案例3)看起来像没有volatile关键字的多线程中的常见程序.假设我在全局变量中添加一些锁定(无volatile关键字):

int var = 0;
int task1()
{
    lock();   // some mutex
    while (var == 0) { do sth }
    release()
}
int task2()
{
    lock();
    var++;
    release();
}
Run Code Online (Sandbox Code Playgroud)

对我来说看起来很正常.那么我volatile在多线程中真的需要吗?为什么我从未见过将volatile限定符添加到变量中以避免以前在多线程程序中进行优化

Kam*_*uri 9

使用volatile关键字的要点是防止编译器生成使用CPU寄存器作为表示变量的更快方式的代码.这会强制编译代码在每次访问变量时访问RAM中的确切内存位置,以获取可能已由另一个实体更改的最新值.通过添加,volatile我们确保我们的代码知道任何其他人(如硬件或ISR)对变量所做的任何更改,并且不会发生一致性问题.

如果没有volatile关键字,编译器会尝试通过将RAM中的变量内容读入CPU寄存器一次并在循环或函数中使用该缓存值来生成更快的代码.访问RAM可能比访问CPU寄存器慢几十倍.

我已经有了第1项和第2项的经验,但我认为你不需要volatile在多线程环境中定义变量.添加锁定/解锁机制是解决同步问题所必需的,与所涉及的内容无关volatile.

  • 此外,'volatile'关键字阻止编译器优化对变量的连续读或写操作.例如:flag = 1; flag = 2; 如果没有'volatile'关键字,编译器会通过忽略第一个赋值来优化代码.但是当您写入硬件寄存器时,将多个值写入同一个寄存器是有意义的,不必跳过任何步骤.反过来说,从一些硬件缓冲区(例如:串口)读取时,连续几次读取"flag"是有意义的.同样,'volatile'关键字会阻止优化. (4认同)
  • 关于多线程的评论是错误的.通常在多线程设置中,您确实需要锁定,但是,您还需要声明变量volatile.锁定是为了确保您没有看到多个访问字宽变量"部分更改",而volatile是为了确保实际更改内存值. (2认同)