我有以下C代码:
/* the memory entry points to can be changed from another thread but
* it is not declared volatile */
struct myentry *entry;
bool isready(void)
{
return entry->status == 1;
}
bool isready2(int idx)
{
struct myentry *x = entry + idx;
return x->status == 1;
}
int main(void) {
/* busy loop */
while (!isready())
;
while (!isready2(5))
;
}
正如我在评论中指出的那样,即使该条目指向的数组可以从另一个线程(甚至实际上直接从内核空间)更改,它也不声明为易失性。
以上代码不正确/不安全吗?我的想法是,不能在isready,isready2的主体中执行优化,并且由于我从main内反复执行函数调用,因此每次调用时都应读取适当的内存位置。
另一方面,编译器可以内联这些函数。
是否可能以导致发生单个读取(因此导致无限循环)而不是多个读取(即使这些读取来自加载/存储缓冲区)的方式来做到这一点?还有第二个问题。是否可以通过仅在某些特定位置强制转换为volatile来防止编译器进行优化?
void func(void)
{
entry->status = 1;
while (((volatile struct myentry *) entry)->status != 2)
;
}
谢谢。
答案 0 :(得分:2)
如果内存entry
指向的内存可以被另一个线程修改,则该程序具有数据争用,因此行为未定义。即使使用volatile
,也是如此。
要让一个变量由多个线程并发访问,在ISO C11中,它必须是atomic type或受到正确同步的保护。
如果使用的是较旧的标准修订版,则标准不提供有关多线程的保证,因此您将受制于编译器的任何特殊行为。
如果使用POSIX线程,则没有可移植的原子操作,但它确实定义了同步原语。
另请参阅:
第二个问题是一个错误,我建议不要这样做,因为不同的编译器可能会以不同的方式解释含义,并且无论哪种方式,行为都仍未正式定义。