将易失性数组与非易失性数组进行比较
Posted
技术标签:
【中文标题】将易失性数组与非易失性数组进行比较【英文标题】:Comparing a volatile array to a non-volatile array 【发布时间】:2015-02-22 16:36:08 【问题描述】:最近我需要比较两个 uint 数组(一个是易失性数组,另一个是非易失性数组),结果令人困惑,一定是我对易失性数组有误解。
在将该数组与全局易失性数组进行比较之前,我需要从输入设备读取一个数组并将其写入一个局部变量。如果有任何区别,我需要将新数组复制到全局数组并将新数组发布到其他平台。代码是打击:
#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
...
由于解决错误时间紧迫,我解决了如下问题:
char arrayStr[ARRAYLENGTH*4];
char arrayStr1[ARRAYLENGTH*4];
array2String(arrayStr,myArray);
array2String(arrayStr1,shadow_array);
if(strCompare(arrayStr,arrayStr1))
publish(arrayStr1);
但是,这种方法远非有效。如果有人有合理的解释,我想听听。
谢谢。
[从 cmets 更新:]
对于 volatile 部分,全局数组必须是 volatile,因为其他线程正在访问它。
【问题讨论】:
对于两个第一个 sn-ps:您确定向我们展示了产生观察到的行为的确切代码吗? readInput的定义是什么?用 -Wall 编译,有没有警告?为什么数组易失?真的可以改变吗? 正如我所说,它不是确切的代码,只是等效的。在原始代码中,我从 uart 接口逐字节读取数组,所以我不想让 sn-ps 真的很长。但比较步骤与原始代码中的步骤相同,只是数组名称不同。 @rici readInput 通过 uart 发送消息并将接收到的字节放入 shadow_array,此外它还检查校验和。但它工作正常。对于 volatile 部分,全局数组必须是 volatile,因为其他线程正在访问它。并且 shadow_array 必须是非易失性的,因为 readInput 方法不接受易失性数组作为参数。 你的真实代码可能看起来像这样:if (!(myArray[i] = shadow_array[i])) change = 1; myArray[i] = shadow_array[i];
【参考方案1】:
如果全局数组是易失的,您的跟踪代码可能不准确:
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];
这会复制比较中使用的值并报告该值。我的直觉是它不会显示出差异,但理论上它可以。
【讨论】:
实际上,只是为了测试两次读取之间是否发生任何事情,我们在您插入“值”的位置插入了两个变量,但它仍在推送更新:这是屏幕截图:prntscr.com/68hcrf 我不确定myArray
到底有多不稳定;对于我作为可能原因显示为实际原因的内容,它必须相当特别不稳定。这很奇怪,因为当你第二次阅读它时,它已经给出了你想要的答案,但第一次没有得到。你在搞乱 UART 之类的东西,你在 cmets 中说,所以可能是你的编译器没有按照你的要求去做。不过,您可能必须查看生成的汇编程序才能知道这一点。存储数组的内存是否有时序属性?你用的是什么平台?
屏幕截图中的代码是for (ii = 0; ii < ARRAYLENGTH; ii++) uint8_t a_ = myArray[ii]; uint8_t b_ = shadow_array[ii]; if (a_ != b_) change = 1; printf("old:%d,new:%d\r\n", a_, b_); myArray[ii] = shadow_array[ii];
,其中没有显示第二个右大括号。清楚显示的代码不应生成新旧相同的行。除了“编译器错误”之外,我没有解释为什么会这样做,但您不太可能第一次在这里遇到问题。
我们也无法给出合理的解释。然后我想可能有一些我们不知道的关于波动性的东西。让我们等待其他人是否有任何解释。如果不是,则可能是由于编译器或优化级别的原因。顺便说一句,大括号已完成,我在截图时忘记包含它。【参考方案2】:
全局数组必须是可变的,因为其他线程正在访问它
正如您“很好地”观察到的那样,声明一个数组 volatile
并不是保护它免受不同线程并发读/写访问的方法。
为此使用互斥锁。例如,通过将对“全局数组”的访问包装到一个锁定和解锁该互斥锁的函数中。那就只能用这个函数访问“全局数组”了。
参考资料:
Why is volatile not considered useful in multithreaded C or C++ programming? https://www.kernel.org/doc/Documentation/volatile-considered-harmful.txt对于printf()
ing unsigned
整数也使用转换说明符u
而不是d
。
【讨论】:
我目前正在度假,我会尝试在我回来时为访问全局数组的函数插入一个互斥锁。我希望它有效。但是,老实说,我并不那么乐观,因为即使我将代码更改为以下内容,也没有任何解释为什么会发生这种情况:prntscr.com/68hcrf【参考方案3】:当变量(或数组)可能在当前程序执行流程之外发生变化时,应将其声明为 volatile。这可能由并发线程或 ISR 发生。 但是,如果只有一个实际写入它的人,而所有其他人都是 jsut Readers,那么实际编写的代码可能会将其视为非易失性(即使没有办法告诉编译器这样做)。
因此,如果比较函数是项目中全局数组实际更改(更新)的唯一点,那么多次读取就没有问题。尽管有 volatile 声明,但可以在(外部)知识的情况下设计代码,即外部源不会发生任何更改。
然而,“读者”确实知道变量(或数组内容)可能会改变并且不会缓冲它读取的内容(例如,通过将读取的值存储在寄存器中以供进一步使用),但仍然是数组内容他们在阅读时可能会发生变化,并且整个信息可能会不一致。 所以建议使用互斥锁是个好主意。 但是,即使没有人从外部弄乱数组,比较循环失败的原始问题也无济于事。
另外,我想知道如果 myArray 仅在本地使用并且发布是通过发送一个指向 ArrayStr 的指针(它是一个指向非易失性字符(数组)的指针来完成),我想知道为什么它被声明为 volatile。 没有理由 myArray 应该是易变的。实际上,它的存在根本没有理由: 只需读入数据,创建一个临时字符串,如果它与原始字符串不同,则替换旧字符串并发布。好吧,总是构建字符串可能效率较低,但它使代码更短并且显然有效。
static char arrayStr[ARRAYLENGTH*4]=;
char tempStr[ARRAYLENGTH*4];
array2String(tempStr,shadow_array);
if(strCompare(arrayStr,tempStr))
strCopy(arrayStr, tempStr);
publish(arrayStr);
【讨论】:
以上是关于将易失性数组与非易失性数组进行比较的主要内容,如果未能解决你的问题,请参考以下文章