gpt4 book ai didi

c - 为什么线程不能识别标志的变化?

转载 作者:太空宇宙 更新时间:2023-11-04 02:25:56 25 4
gpt4 key购买 nike

我在 Windows 7 平台上的 C/Visual Studio 下遇到了一个奇怪的情况。不时出现问题,我花了很多时间才找到它。问题出在第三方库中,我有完整的代码。创建了一个线程(printLog 语句来 self 自己):

    ...
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 在我们这个时代是无用的,不再需要了,除非在这种情况下,内存位置被外围设备改变......

最佳答案

在 C11 之前,这完全取决于平台,因为您观察到的效果取决于您的平台使用的内存模型。这与编译器优化不同,因为线程之间的同步点需要编译器插入屏障指令,而不是例如将某些东西设为常量。对于第 7.17.3 节的 C11,指定了不同的模型。所以你的值没有被静态优化掉,线程 A 只是从不读取线程 B 写的值,但仍然有它的本地值。

实际上,许多项目尚未使用 C11,因此您可能需要查看平台的文档。请注意,在许多情况下,您不必修改标志变量的类型(以防万一)。大多数内存模型都指定了同步点,这些同步点也禁止对某些指令进行重新排序,即:

int x = 3;
_Atomic int a = 1;
x = 5;
a = 2;

编译器通常必须确保当 a 的值为 1 时 x 的值为 3,并且当 a 被赋值时值 2,x 将具有值 5。volatile 不参与此关系(在 C/C++ 11 模型中 - 经常混淆,因为它确实参与了 Java 的发生-before),并且大部分是无用的,除非你的写入永远不会被优化,因为它们有副作用,比如编译器无法理解的 LED 闪烁:

volatile int x = 1; // some special location - blink then clear 
x = 1; // blink then clear
x = 1; // blink then clear

关于c - 为什么线程不能识别标志的变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51241727/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com