将易失性数组与非易失性数组进行比较

时间:2015-02-22 16:36:08

标签: c arrays multithreading comparison volatile

最近我需要比较两个uint数组(一个是volatile和其他非易失性),结果令人困惑,我不得不对易失性数组有所误解。

我需要从输入设备读取数组并将其写入局部变量,然后再将此数组与全局易失性数组进行比较。如果有任何差异,我需要将新的一个复制到全局一个并将新阵列发布到其他平台。代码就像打击一样:

#define ARRAYLENGTH 30
volatile uint8 myArray[ARRAYLENGTH];

void myFunc(void){
    uint8 shadow_array[ARRAYLENGTH],change=0;
    readInput(shadow_array);
    for(int i=0;i<ARRAYLENGTH;i++){
        if(myArray[i] != shadow_array[i]){
            change = 1;
            myArray[i] = shadow_array[i];
            }
        }
    if(change){
        char arrayStr[ARRAYLENGTH*4];
        array2String(arrayStr,myArray);
        publish(arrayStr);
        }
    }

但是,这不起作用,每次myFunc运行时,都会发布一条新消息,大部分与之前的消息相同。

所以我在代码中插入了一个日志行:

for(int i=0;i<ARRAYLENGTH;i++){
    if(myArray[i] != shadow_array[i]){
        change = 1;
        log("old:%d,new:%d\r\n",myArray[i],shadow_array[i]);
        myArray[i] = shadow_array[i];
        }
    }

我得到的日志如下:

old:0,new:0
old:8,new:8
old:87,new:87
...

由于解决bug是时间紧迫的,我解决了以下问题:

char arrayStr[ARRAYLENGTH*4];
char arrayStr1[ARRAYLENGTH*4];
array2String(arrayStr,myArray);
array2String(arrayStr1,shadow_array);
if(strCompare(arrayStr,arrayStr1)){
    publish(arrayStr1);
    }
}

但是,这种方法远非效率。如果有人有合理的解释,我想听听。

谢谢。


[更新自评论:]

对于易失性部分,全局数组必须是易失性的,因为其他线程正在访问它。

3 个答案:

答案 0 :(得分:3)

如果全局数组是易失性的,那么您的跟踪代码可能不准确:

for(int i=0;i<ARRAYLENGTH;i++){
    if(myArray[i] != shadow_array[i]){
        change = 1;
        log("old:%d,new:%d\r\n",myArray[i],shadow_array[i]);
        myArray[i] = shadow_array[i];
        }
    }

问题是比较行读取myArray[i]一次,但是日志消息再次读取它,并且因为它是易失性的,所以不能保证两个读取将给出相同的值。准确的记录技术将是:

for (int i = 0; i < ARRAYLENGTH; i++)
{
    uintu_t value;
    if ((value = myArray[i]) != shadow_array[i])
    {
        change = 1;
        log("old:%d,new:%d\r\n", value, shadow_array[i]);
        myArray[i] = shadow_array[i];
    }
}

这会复制比较中使用的值并报告。我的直觉是它不会显示出差异,但理论上它可以。

答案 1 :(得分:1)

  

全局数组必须是易失性的,因为其他线程正在访问它

正如您“很好”地观察到声明数组volatile不是保护它免受不同线程的并发读/写访问的方法。

使用互斥锁。例如,通过将对“全局数组”的访问包装到锁定和解锁此互斥锁的函数中。然后只使用此函数来访问“全局数组”。

参考文献:


同样对于printf() unsigned个整数,请使用转化说明符u而不是d

答案 2 :(得分:0)

当变量(或数组)在当前程序执行流程之外可能更改时,应将其声明为volatile。这可能由并发线程或ISR发生。 但是,如果只有一个实际编写它的人,而其他所有人都是jsut读者,那么实际的编写代码可能会将其视为不稳定的(即使没有办法告诉Compiler这样做)。

因此,如果比较功能是项目中唯一实际更改(更新)了gloal Array的点,则多次读取没有问题。可以使用(外部)知识来设计代码,尽管声明是可变的,但外部源不会进行任何更改。

但是,“读取器”确实知道变量(或数组内容)可能会更改并且不会缓冲其读取的内容(例如,通过将读取的vlaue存储在寄存器中以供进一步使用),但是数组内容仍然在阅读时可能会更改,并且整个信息可能会不一致。 因此,建议使用互斥锁是一个好主意。 但是,即使没有人从外面弄乱数组,它也无法解决比较循环失败的原始问题。

此外,我不知道为什么myArray仅在本地使用,并且通过发送指向ArrayStr的指针(这是指向非易失性char(数组)的指针)来声明为volatile。 没有理由为什么myArray应该是易失的。实际上,根本没有任何理由存在: 只需读入数据,创建一个临时调整,如果与原始调整不同,则替换旧字符串并发布它。好吧,始终构建字符串可能效率不高,但这会使代码短得多,并且显然可以正常工作。

static char arrayStr[ARRAYLENGTH*4]={};
char tempStr[ARRAYLENGTH*4];
array2String(tempStr,shadow_array);
if(strCompare(arrayStr,tempStr)){
    strCopy(arrayStr, tempStr);
    publish(arrayStr);
    }
}