【发布时间】:2018-07-09 09:02:58
【问题描述】:
我在 Windows 7 平台上的 C/Visual Studio 下遇到了一个奇怪的情况。时不时出现问题,我花了很多时间才找到它。问题出在第三方库中,我有完整的代码。创建了一个线程(printLog语句来自我自己):
...
plafParams->eventThreadFlag = 2;
printLog("before CreateThread");
if (plafParams->hReadThread_p = CreateThread(NULL, 0, ( LPTHREAD_START_ROUTINE ) plafPortReadThread, ( void * ) dlmsInstance, 0,
&plafParams->portReadThreadID) )
{
printLog("after CreateThread: OK");
plafParams->eventThreadFlag = 3;
}
else
{
unsigned int lasterr = GetLastError();
printLog("error CreateThread, last error:%x", lasterr);
/* Could not create the read thread. */
...
...
return FAILURE;
}
printLog("SUCCESS");
...
...
线程函数为:
void *plafPortReadThread(DLMS_GLOBALS *dlmsInstance)
{
PLAF_PARAMS *plafParams;
plafParams = (PLAF_PARAMS *)(dlmsInstance->plafParams);
printLog("start - plafPortReadThread, plafParams->eventThreadFlag=%x", plafParams->eventThreadFlag);
while ((plafParams->eventThreadFlag != 1) && (plafParams->eventThreadFlag != 3))
{
if (plafParams->eventThreadFlag == 0)
{
printLog("exit 1 - plafPortReadThread, plafParams->eventThreadFlag=%x", plafParams->eventThreadFlag);
CloseHandle(plafParams->hReadThread_p);
plafFree((void **)&plafParams);
ExitThread(0);
break;
}
}
printLog("start - plafPortReadThread, proceed=%d", proceed);
...
现在,当在线程内启动 while 循环之前设置标志时,一切正常:
SUCCESS
start - plafPortReadThread, plafParams->eventThreadFlag=3
但有时线程足够快,所以 while 循环在标志实际设置在外部之前启动。
那么输出是:
start - plafPortReadThread, plafParams->eventThreadFlag=2
SUCCESS
最令人惊讶的是,while 循环没有退出,即使在标志设置为 3 之后也是如此。
似乎编译器“优化”了标志并假设它不能从外部更改。
可能是什么问题?我真的很惊讶。还是我已经完全监督了其他事情?我知道,代码不是很优雅,最好用信号量或信号来完成这些事情。但这不是我的代码,我想尽可能少地改变。
删除整个 while 条件后,它按预期工作。
我应该将结构或其字段更改为 volatile 吗?每个人都说,volatile 在我们这个时代是没用的,不再需要了,除非内存位置被外围设备改变......
【问题讨论】:
-
行为观察确实是一种优化。
volatile不是没用的,这可能就是你需要的。但可能这还不够,您需要锁/原子标志来确保正确访问共享数据。 -
我不知道 - 编译器肯定不应该完全优化它吗?如果这确实是原因,Volatile 应该对其进行分类,但我们不应该再猜测编译器忽略了多少我们的代码......另外,为什么不记录帖子创建线程记录消息?
-
从技术上讲,我相信它需要
_Atomic而不是volatile。没有_Atomic它应该是UB,但我仍然认为它应该在通用平台(x86)上工作。 -
volatile在一般情况下对您没有帮助(例如,在处理在不同内核上运行的线程时)。您需要的是memory barrier。通常使用互斥锁或信号量。 -
最有趣的是,即使在答案中也没有关于“易失性”的同质观点。现在是不是没用了?
标签: c multithreading compiler-optimization volatile