函数没有易失性。怎么修?

Posted

技术标签:

【中文标题】函数没有易失性。怎么修?【英文标题】:function doesn't have volatile. How to fix? 【发布时间】:2012-07-25 05:11:23 【问题描述】:

我在 2 个线程之间共享一个变量。我使用 volatile 来避免优化。

但是,它显示了 strcpy 中没有 volatile 的错误。 (如下)

如何正确修复此错误?

有人告诉我要键入 volatile。 但是如果我抛弃了 volatile,那么我就失去了 volatile 的目的...... 最终可能会因优化而出现运行时错误.....不是吗?

非常感谢。

(代码可以直接编译)


CRITICAL_SECTION CriticalSection;

HANDLE hEvent;

void __cdecl MyThread(void* name)



char serName[256];

volatile char* vptr = (char*) name;



EnterCriticalSection(&CriticalSection);



strcpy(serName, vptr); // error : cannot convert 'volatile'

// use (and not modify) name…



LeaveCriticalSection(&CriticalSection);

SetEvent (hEvent) ;






void main ()



char name[256] = "abcde";

hEvent = CreateEvent (NULL, false, false, NULL) ;

if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) )

return;



_beginthread (MyThread, 0, name) ;



EnterCriticalSection(&CriticalSection);

// access name…

LeaveCriticalSection(&CriticalSection);



WaitForSingleObject (hEvent, INFINITE) ;

DeleteCriticalSection(&CriticalSection);

CloseHandle (hEvent);

system("pause");



另一方面,我可以编写自己的 strcpy 来支持 volatile。 但这很奇怪。 因为如果是这样,那么我每次使用 volatile 时都必须编写自己的 I/O 流(或那些复杂的函数)?

再次感谢您的回答。

【问题讨论】:

简单回答:删除volatile。在任何情况下你都想避免什么优化? 顺便说一句,volatile 对线程安全没有帮助。 @Yochai:在 Visual C++ 中确实如此,而且这个问题被如此标记。 我认为 Charles Bailey 和 6502 是对的。我的代码中不需要“易失性”。我已经使用临界区来防止波动。 【参考方案1】:

显然,您对“易失性”的含义没有清楚的理解。含义或多或少是“嘿编译器,请注意其他人将更改此变量,因此您不能假设除非您的代码将其写入,否则该值将保持不变。此外,其他人可能正在监视此变量,因此当我写信给这个变量请不要对无关紧要的假设做奇怪的事情,因为对于那些关注它的其他人来说很重要,所以只要写下我想让你写的内容以及我告诉你这样做的时间。”

什么时候使用“volatile”很重要?下面是一个常见的例子:

volatile int stopflag; // flag will be set by an interrupt handler

void mainloop()

    stopflag = 0;
    while (!stopflag)
    
       ...
    

如果三个点中的代码从不接触stopflag,并且从不调用具有未知实现的函数,那么编译器可能会避免读取循环中的标志,因为查看代码本身似乎有根本不需要读取变量...只需设置并永远循环。

另一种情况可能是:

extern volatile unsigned char outloc; // monitored by hardware

...
// emit a wave pulse
for (int x=0; x<256; x++)
    outloc = x;

如果没有volatile,编译器可能会尝试将0xFF 写入该位置,而不是写入所有中间值。

请注意,在现代硬件上使用volatile 进行线程同步是不够的。在今天的计算机中,CPU 通常是多核的,因此写入和读取不再是原子操作。虽然过去(在单核 CPU 上)实际上通常可以使用 volatile 变量进行线程同步,但现在这是错误的。

您似乎确实有兴趣告诉 缓冲区 确实正在被其他人读写,但在这种情况下,缓冲区的地址是公共的,因此在任何函数调用时(除非它被内联或具有已知的实现代码)编译器必须假定缓冲区内容可能已更改或将被未知代码读取。

我敢打赌,线程同步原语是以正确的方式声明的,以确保在您的情况下也是如此(即使 strcpy 是内联的)。

【讨论】:

“您似乎确实有兴趣告诉其他人确实正在读取和写入缓冲区” 【参考方案2】:

我可以理解,在某些地方(在使用线程时)我们不希望优化发生,为此我们使用“volatile”关键字。

请参考MSDN

要将指针指向的对象声明为 const 或 volatile,请使用以下形式的声明:

const    char *cpch;
volatile char *vpch;

要将指针的值(即存储在指针中的实际地址)声明为 const 或 volatile,请使用以下形式的声明:

char * const    pchc;
char * volatile pchv;

所以,你可以使用(根据你的需要)

"strcpy(serName, (char *)vptr);"  or " char*  volatile vptr = (char*) name;"

以上是通过抑制优化运行代码的解决方案,也不会造成任何错误。现在您将不需要自己实现完整的 I/O :)

这对我有用。希望对你也有帮助……

【讨论】:

以上是关于函数没有易失性。怎么修?的主要内容,如果未能解决你的问题,请参考以下文章

易失性用户定义函数未按预期重新计算(VBA/Excel)

将易失性数组转换为非易失性数组

自动重新评估非易失性 UDF

ESP-C3入门4. NVS非易失性存储使用

ESP-C3入门3. NVS非易失性存储使用

ESP-C3入门4. NVS非易失性存储使用