我想知道,不管编译器如何优化,取消引用指针是否总是会被转换为机器级的Load/Store指令.

假设我们有两个线程,一个(让我们称之为Tom)接收用户输入并编写一个bool变量.该变量由另一个人(这位是曾傑瑞)读取,以决定是否继续循环.我们知道,优化编译器在编译循环时可能会将变量存储在寄存器中.因此,在运行时,曾傑瑞可能会读取与Tom实际编写的值不同的过时值.因此,我们应该将bool变量声明为volatile.

但是,如果解引用指针总是会导致内存访问,那么这两个线程可以使用指针来引用变量.在每次写入时,Tom将通过取消引用指针并向其写入来将新值存储到内存中.在每次阅读时,曾傑瑞都可以通过取消引用同一指针来真正阅读汤姆所写的内容.这似乎比依赖于实现的volatile

我是多线程编程的新手,所以这个 idea 可能看起来微不足道,也不必要.但我真的很好奇.

推荐答案

取消引用指针是否总是会导致内存访问?

否,例如:

int five() {
    int x = 5;
    int *ptr = &x;
    return *ptr;
}

任何正常的优化编译器都不会从这里的堆栈内存中发出mov,而是类似以下内容:

five():
  mov eax, 5
  ret

这是允许的,因为as-if rule.

How do I do inter-thread communication through a bool* then?

这就是std::atomic<bool>的用处. 您不应该使用非原子对象在线程之间进行通信,因为同时通过两个线程访问同一内存在C++中是未定义的行为.std::atomic使其成为线程安全的. 例如:

void thread(std::atomic<bool> &stop_signal) {
    while (!stop_signal) {
        do_stuff();
    }
}

Technically,这并不意味着从stop_signal开始的每个加载实际上都会发生.允许编译器执行部分循环展开,如下所示:

void thread(std::atomic<bool> &stop_signal) {
    // only possible if the compiler knows that do_stuff() doesn't modify stop_signal
    while (!stop_signal) {
        do_stuff();
        do_stuff();
        do_stuff();
        do_stuff();
    }
}

原子load()被允许观察陈旧的值,因此编译器可以假设四个load()都将读取相同的值. 只有某些操作,如fetch_add(),才需要观察最新的值. 即使到那时,这种优化也是可能的.

实际上,这样的优化在任何编译器中都不是针对std::atomic实现的,因此std::atomic是准volatile.这同样适用于C的atomic_bool,以及一般的_Atomic类型.


另见:

C++相关问答推荐

修改pGM使用指针填充2-D数组但不起作用

exit(EXIT_FAILURE):Tcl C API类似功能

DPDK-DumpCap不捕获端口上的传入数据包

为什么双重打印与C中的float具有不同的大小时具有相同的值?

警告:C++中数组下标的类型为‘char’[-Wchar-subpts]

使用错误的命令执行程序

在C++中访问双指针

如何有效地编写代码来判断两个元素数量相同的数组即使在不同的位置也具有相同的元素?

在C程序中使用Beaglebone Black UART的问题

int * 指向int的哪个字节?

使用C++中的字符串初始化 struct 时,从‘char*’初始化‘char’使指针变为整数,而不进行强制转换

宏观;S C调深度

c程序,让用户输入两类数字,并给出输出用户输入多少个数字

计算SIZE_MAX元素的长数组的大小

即使我在C++中空闲,也肯定会丢失内存

可以';t从A9G模块拨打电话

如何解释数组中的*(ptr)和*(ptr+2)?

struct 中的qsort,但排序后的 struct 很乱

为什么<到达*时不会转换为>?

如何在 C 中的 Postgres 函数的表中 for 循环