Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 现在C和C+的线程保证是什么+;编译器?_C++_C_Multithreading - Fatal编程技术网

C++ 现在C和C+的线程保证是什么+;编译器?

C++ 现在C和C+的线程保证是什么+;编译器?,c++,c,multithreading,C++,C,Multithreading,我想知道编译器如何保证线程对内存的写入在其他线程中有明显的效果 我知道无数这样的案例是有问题的,我相信如果你有兴趣回答,你也知道,但请关注我将要介绍的案例 更准确地说,我关心的是可能导致线程丢失由其他线程执行的内存更新的情况。我不在乎(在这一点上)更新是非原子的还是同步性差:只要相关线程注意到这些更改,我就会很高兴 我希望编译器能够区分两种变量访问: 访问必须有地址的变量 访问不一定有地址的变量 例如,如果您使用此代码段: void sleepingbeauty() { int i

我想知道编译器如何保证线程对内存的写入在其他线程中有明显的效果

我知道无数这样的案例是有问题的,我相信如果你有兴趣回答,你也知道,但请关注我将要介绍的案例

更准确地说,我关心的是可能导致线程丢失由其他线程执行的内存更新的情况。我不在乎(在这一点上)更新是非原子的还是同步性差:只要相关线程注意到这些更改,我就会很高兴

我希望编译器能够区分两种变量访问:

  • 访问必须有地址的变量
  • 访问不一定有地址的变量
例如,如果您使用此代码段:

void sleepingbeauty()
{
    int i = 1;
    while (i) sleep(1);
}
由于
i
是一个局部变量,我假设我的编译器可以对它进行优化,让睡美人永远沉睡

void onedaymyprincewillcome(int* i);

void sleepingbeauty()
{
    int i = 1;
    onedaymyprincewillcome(&i);
    while (i) sleep(1);
}
由于
i
是一个局部变量,但它的地址被获取并传递给另一个函数,因此我假设我的编译器现在知道它是一个“可寻址”变量,并生成对它的内存读取,以确保可能有一天王子会来

int i = 1;
void sleepingbeauty()
{
    while (i) sleep(1);
}
由于
i
是一个全局变量,因此我假设编译器知道变量有一个地址,并将生成对该变量的读取,而不是缓存该值

void sleepingbeauty(int* ptr)
{
    *ptr = 1;
    while (*ptr) sleep(1);
}
我希望解引用操作符足够显式,以便编译器在每次循环迭代时生成内存读取

我确信这是每个C和C++编译器在生产中使用的内存访问模型,但我认为没有任何保证。事实上,C++03甚至对线程的存在视而不见,因此考虑到标准,这个问题甚至没有意义。不过,我对C不太确定

是否有一些文档说明我是对的还是错的?我知道这些都是浑水,因为这些可能不符合标准,这对我来说似乎是一个重要的问题


除了编译器生成读取之外,我还担心CPU缓存在技术上可能会保留过时的值,而且即使我的编译器尽了最大努力来实现读取和写入,这些值在线程之间也永远不会同步。这种情况会发生吗?

您对过度优化的唯一控制是
volatile

编译器对并发线程在同一时间访问同一位置的情况进行限制。您将需要使用某种类型的锁定机制。

您假定错误

void onedaymyprincewillcome(int* i);

void sleepingbeauty()
{
    int i = 1;
    onedaymyprincewillcome(&i);
    while (i) sleep(1);
}
在这段代码中,编译器将每次通过循环从内存加载
i
。为什么?不是因为它认为另一个线程可以改变它的值,而是因为它认为
sleep
可以修改它的值。它与
i
是否有地址或必须有地址无关,与此线程执行的可能修改代码的操作有关

特别是,不能保证分配给
int
甚至是原子的,尽管这在我们现在使用的所有平台上都是正确的

如果没有为线程化程序使用适当的同步原语,就会出现太多问题。比如说,

char *str = 0;
asynch_get_string(&str);
while (!str)
    sleep(1);
puts(str);
这有时会(甚至会在某些平台上)打印出完全的垃圾并使程序崩溃。它看起来很安全,但是因为您没有使用正确的同步原语,所以在更改它所引用的内存位置之前,线程可以看到对
ptr
的更改,即使另一个线程在设置指针之前初始化了字符串

所以不要,不要,不要做这种事。不,
volatile
不是一个解决方案


摘要:基本问题是编译器只更改指令的顺序以及加载和存储操作的位置。这通常不足以保证线程安全,因为处理器可以自由更改加载和存储的顺序,并且加载和存储的顺序不会在处理器之间保留。为了确保事情按正确的顺序发生,你需要记忆障碍。您可以自己编写程序集,也可以使用互斥体/信号量/关键部分等,这对您来说是正确的。

虽然C++98和C++03标准没有规定编译器必须使用的标准内存模型,但C++0x规定了,您可以在此处阅读:

最后,对于C++98和C++03,这实际上取决于编译器和硬件平台。通常情况下,编译器不会对正常编写的代码发出任何内存屏障或围栏操作,除非您使用编译器内部文件或操作系统标准库中的某些文件进行同步。大多数互斥体/信号量实现还包括内置内存屏障操作,以防止CPU在互斥体上的锁定和解锁操作中进行推测性读写,以及防止编译器在相同的读写调用中对操作进行任何重新排序

最后,正如Billy在评论中指出的那样,在Intel x86和x86_64平台上,单字节增量中的任何读写操作都是原子操作,以及对x86上的任何4字节对齐内存位置和x86_64上的4或8字节对齐内存位置的寄存器值的读写。在其他平台上,情况可能并非如此,您必须查阅该平台的文档

访问不一定有地址的变量


所有变量都必须有地址(从语言的角度来看——编译器可以避免提供地址,但从语言内部看不到地址)。是希德