C 关于(非)易失性和优化编译器的问题

C 关于(非)易失性和优化编译器的问题,c,multithreading,volatile,C,Multithreading,Volatile,我有以下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

我有以下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)) 
        ; 
}
正如我在注释中所注意到的,条目不是声明为volatile的,即使它指向的数组可以从另一个线程更改(或者实际上甚至可以直接从内核空间更改)

上述代码是否不正确/不安全?我的想法是,在isready、isready2的主体中无法执行任何优化,并且由于我在main中重复执行函数调用,因此每次调用都应该读取相应的内存位置

另一方面,编译器可以内联这些函数。它是否可能以导致单次读取(从而导致无限循环)而不是多次读取(即使这些读取来自加载/存储缓冲区)的方式进行

还有第二个问题。是否可以通过仅在某些地方强制转换为volatile来防止编译器进行优化

void func(void)
{
    entry->status = 1;
    while (((volatile struct myentry *) entry)->status != 2)
        ;
}

谢谢。

如果内存
条目
指向另一个线程可以修改,则程序存在数据争用,因此行为未定义。即使使用了
volatile
,这仍然是正确的

在ISO C11中,要使一个变量由多个线程并发访问,它必须是,或者由正确的同步保护

如果使用较旧的标准版本,则该标准没有提供关于多线程的保证,因此编译器的任何特殊行为都会影响您

如果使用POSIX线程,则没有可移植的原子操作,但它确实定义了同步原语

另见:


第二个问题是一个bug,我建议不要这样做,因为不同的编译器可能会对其含义进行不同的解释,而且无论哪种方式,其行为在形式上都是未定义的。

如果内存
项指向另一个线程可以修改,然后程序有一个数据竞争,因此行为是未定义的。即使使用了
volatile
,这仍然是正确的

在ISO C11中,要使一个变量由多个线程并发访问,它必须是,或者由正确的同步保护

如果使用较旧的标准版本,则该标准没有提供关于多线程的保证,因此编译器的任何特殊行为都会影响您

如果使用POSIX线程,则没有可移植的原子操作,但它确实定义了同步原语

另见:


第二个问题是一个bug,我建议不要这样做,因为不同的编译器可能会对其含义进行不同的解释,而且无论哪种方式,行为在形式上都是未定义的。

上述代码是否不正确/不安全?是的,不安全。无法保证对
条目的更新是原子的。如果在更新的中间读取<代码>条目<代码>,值可能是无稽之谈。假设“无法执行优化”,可能还有很多其他问题。@chux说得对。它指向的内存可以更改,而不是指针的值。@AndrewHenle状态字段的更新前面有一个内存屏障,因此它是最后一个要更新的字段。状态更改后,不会再对条目进行更新(直到状态再次更改)。这会改变情况吗?
它没有声明为volatile
-->为什么不更改
struct myentry*条目-->
volatile struct myentry*条目?@chux我只是想知道它是否真的“正确”。例如,Linus在这里说了以下()似乎是有道理的:““在C语言中,是“数据”是易变的,但这是疯狂的。数据不是易变的-访问是易变的。所以说“让这个特定的访问小心点”,而不是“让所有对这个数据的访问都使用一些随机策略”可能是有道理的。”“上面的代码不正确/不安全吗?”?是的,不安全。无法保证对
条目的更新是原子的。如果在更新的中间读取<代码>条目<代码>,值可能是无稽之谈。假设“无法执行优化”,可能还有很多其他问题。@chux说得对。它指向的内存可以更改,而不是指针的值。@AndrewHenle状态字段的更新前面有一个内存屏障,因此它是最后一个要更新的字段。状态更改后,不会再对条目进行更新(直到状态再次更改)。这会改变情况吗?
它没有声明为volatile
-->为什么不更改
struct myentry*条目-->
volatile struct myentry*条目?@chux我只是想知道它是否真的“正确”。例如,Linus在这里说了以下()似乎是有道理的:““在C语言中,是“数据”是易变的,但这是疯狂的。数据不是易变的-访问是易变的。所以说“让这个特定的访问小心点”,而不是“让所有对这个数据的访问都使用一些随机策略”可能是有道理的。”“让我更详细地解释我的情况。”。我只有两个线程A,B。A只发布条目,B只异步使用它们。B不断扫描阵列(基本上是轮询),检查条目是否已提交状态,在这种情况下,会使用该条目并将其状态更改为自由。线程A还扫描数组以查找空闲条目,更新其字段,执行内存屏障,然后将状态更改为已提交(从而确保状态更改最后写入)。我认为在这种情况下不可能有数据竞争,我只担心在状态字段上进行轮询。@ilstam我无法在没有看到的情况下评论同步代码的正确性,也许你可以发布一个包含实际代码的新问题。我忘了说线程B在更改状态之前也使用了内存障碍。因此不存在种族条件