Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.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++ 同一线程上的两个连续内存\u顺序\u释放存储是否可以相互重新排序?_C++_Multithreading_Memory Barriers_Memory Model_Stdatomic - Fatal编程技术网

C++ 同一线程上的两个连续内存\u顺序\u释放存储是否可以相互重新排序?

C++ 同一线程上的两个连续内存\u顺序\u释放存储是否可以相互重新排序?,c++,multithreading,memory-barriers,memory-model,stdatomic,C++,Multithreading,Memory Barriers,Memory Model,Stdatomic,同一线程上的两个连续内存\u顺序\u释放存储是否可以相互重新排序?无论是从同一线程的角度还是从加载它们的不同线程的角度 报告说: 使用此内存顺序的存储操作执行释放 操作:当前线程中的任何读取或写入都不能重新排序 在这家商店之后 所以在这个例子中: std::atomic<uint64_t> a; std::atomic<uint64_t> b; // ... a.store(0xDEADBEFF, std::memory_order::memory_order_rel

同一线程上的两个连续内存\u顺序\u释放存储是否可以相互重新排序?无论是从同一线程的角度还是从加载它们的不同线程的角度

报告说:

使用此内存顺序的存储操作执行释放 操作:当前线程中的任何读取或写入都不能重新排序 在这家商店之后

所以在这个例子中:

std::atomic<uint64_t> a;
std::atomic<uint64_t> b;

// ...

a.store(0xDEADBEFF, std::memory_order::memory_order_release);
b.store(0xBEEFDEAD, std::memory_order::memory_order_release);
我希望a存储不能在b存储之后重新排序。然而,也许b存储仍然可以在a存储之前重新排序,这是等效的吗?我不知道怎样读这门语言

换言之:文件似乎表明a店不能下移。它是否也能保证b不能上移

我试图确定在另一个线程上,如果我保证看到a是0xDeadBeefDead,我是否获得b并看到0xBEEFDEAD,然后获得a

1在2之前排序。2与3同步,3在4之前排序。这意味着1发生在4之前。根据[intro.races]p18,假设a没有其他修改,4必须从1取其值,即断言永远不会触发


1在2之前排序。2与3同步,3在4之前排序。这意味着1发生在4之前。通过[intro.races]第18页,假设a没有其他修改,4必须从1取其值,即断言永远不会触发。

内存操作(如读写)的重新排序概念通常用于使线程间内存可见性问题更加具体,因为重新安排任务对于任何阻止和取消阻止要做的事情的人来说都是一个日常问题。但它不是线程间通信和内存可见性的基础。顺便说一下,内存顺序值是关于可见性而不是顺序的。不要使用术语记忆顺序

释放语义是通过对任何可以看到存储值的线程的承诺来定义的。这就是为什么释放只是共享变量修改的属性;读取原子对象,即使具有内存顺序cst内存可见性,也不可能是释放操作

看到发布操作的写入值的线程可以假定以前的操作已经完成。共享对象上必须完成的这些操作是读写操作,还有其他一些东西,比如你的源代码忘记提到的对象的构造。以前在程序执行顺序中,甚至在不同的线程中使用相同的finished属性进行的操作可以被视为是由对写入值执行读获取的线程完成的。如果您进行了轻松阅读,您可以在阅读后使用acquire屏障来获取acquire read语义

需要注意的是,release和acquire操作是边界并确定操作的互斥性,就像使用互斥锁一样:原子对象用于获得写入线程和读取线程之间的互斥性

a.store(0xDEADBEFF, std::memory_order::memory_order_release);
a的存储不必具有任何特定的可见性,因为假设我们处于并行化的开始阶段,则没有以前的内存操作来使其可见

b.store(0xBEEFDEAD, std::memory_order::memory_order_release);
b上的一个释放操作很重要:编译器无法对内容重新排序的原因是其他线程可以读取不是线程专用变量的b,并且可以看到特定值0xBEEFDEAD并可能得出释放发生的结论,并使用acquire semantic来保证互斥:

商店发布前的东西 加载后的填充 也就是说,只有当用户代码检查该值是否已写入,并且只有当该值可能来自该值时。因此,本质上用户代码实现互斥协议,但最终编译器使其工作

关于报价:

关于CPP参考的文件说明:

使用此内存顺序的存储操作执行释放 操作:当前线程中的任何读取或写入都不能重新排序 在这家商店之后

我可以很容易地给出至少三种允许重新排序的情况

第一个也是最明显的一个是编译器总是对函数调用进行重新排序:修改一个从其他任何地方都无法访问的纯局部变量和一个外部调用。很明显,这甚至不能通过像屏障这样的特定呼吁来预防,因为这是一种普遍的转变

其他的是不能通过外部函数调用进行的转换,但是编译器知道原子操作,这与单独编译函数的调用不同:

严格函数局部线程通信原语的任何动作,无论是互斥还是原子变量,都可以用任何东西重新排序,因为其他线程无法观察或与变量交互; 当对原子对象A进行操作时,如果值为s,编译器可以看到该对象上的所有操作 tored永远不会更改,它会保留其原始值,然后可以对另一个对象上的任何操作进行重新排序,例如,使用。
这些可能是非常无趣和愚蠢的谁使用互斥作为局部变量?特殊情况,但它们在逻辑上是存在的。

内存操作(如读写操作)的重新排序概念通常用于使线程间内存可见性问题更加具体,因为对任何阻止或取消阻止要做的事情的人来说,任务的重新排序每天都是一个问题。但它不是线程间通信和内存可见性的基础。顺便说一下,内存顺序值是关于可见性而不是顺序的。不要使用术语记忆顺序

释放语义是通过对任何可以看到存储值的线程的承诺来定义的。这就是为什么释放只是共享变量修改的属性;读取原子对象,即使具有内存顺序cst内存可见性,也不可能是释放操作

看到发布操作的写入值的线程可以假定以前的操作已经完成。共享对象上必须完成的这些操作是读写操作,还有其他一些东西,比如你的源代码忘记提到的对象的构造。以前在程序执行顺序中,甚至在不同的线程中使用相同的finished属性进行的操作可以被视为是由对写入值执行读获取的线程完成的。如果您进行了轻松阅读,您可以在阅读后使用acquire屏障来获取acquire read语义

需要注意的是,release和acquire操作是边界并确定操作的互斥性,就像使用互斥锁一样:原子对象用于获得写入线程和读取线程之间的互斥性

a.store(0xDEADBEFF, std::memory_order::memory_order_release);
a的存储不必具有任何特定的可见性,因为假设我们处于并行化的开始阶段,则没有以前的内存操作来使其可见

b.store(0xBEEFDEAD, std::memory_order::memory_order_release);
b上的一个释放操作很重要:编译器无法对内容重新排序的原因是其他线程可以读取不是线程专用变量的b,并且可以看到特定值0xBEEFDEAD并可能得出释放发生的结论,并使用acquire semantic来保证互斥:

商店发布前的东西 加载后的填充 也就是说,只有当用户代码检查该值是否已写入,并且只有当该值可能来自该值时。因此,本质上用户代码实现互斥协议,但最终编译器使其工作

关于报价:

关于CPP参考的文件说明:

使用此内存顺序的存储操作执行释放 操作:当前线程中的任何读取或写入都不能重新排序 在这家商店之后

我可以很容易地给出至少三种允许重新排序的情况

第一个也是最明显的一个是编译器总是对函数调用进行重新排序:修改一个从其他任何地方都无法访问的纯局部变量和一个外部调用。很明显,这甚至不能通过像屏障这样的特定呼吁来预防,因为这是一种普遍的转变

其他的是不能通过外部函数调用进行的转换,但是编译器知道原子操作,这与单独编译函数的调用不同:

严格函数局部线程通信原语的任何动作,无论是互斥还是原子变量,都可以用任何东西重新排序,因为其他线程无法观察或与变量交互; 当对原子对象A进行操作时,编译器可以看到其上的所有操作,如果存储的值从未更改,它将保留其原始值,则可以对另一个对象上的任何操作进行重新排序,例如使用A上的发布存储。
这些可能是非常无趣和愚蠢的谁使用互斥作为局部变量?特殊情况,但它们在逻辑上是存在的。

这怎么可能?@curiousguy:我不确定是否有两种类型的重新排序,一种是向上移动,另一种是向下移动,我们只禁止将a移动到b下方,而不禁止将b移动到a上方,或者,如果语言涵盖了两种看待它的方式。@curiousguy好吧,它让解释变得模棱两可;我们的记忆顺序页面充其量就是我们对孩子们说的谎言。这可能需要一些工作。如果b在当前线程中的a之前重新排序,那么a.store将在当前线程中的b.store之后重新排序,但是a.store不能在b.store之后重新排序。这怎么可能呢?@curiousguy:我不确定是否有两种类型的重新排序,一种是向上移动,另一种是向下移动,我们只禁止将a移到b之下,而不禁止将b移到a之上,或者如果语言涵盖了两种看待它的方式
民族模糊;我们的记忆顺序页面充其量就是我们对孩子们说的谎言。这可能需要一些工作。如果b在当前线程中的a之前重新排序,那么a.store将在当前线程中的b.store之后重新排序,但是a.store在b.store之后不能重新排序。storeBy never fire你的意思是never call abort?当你说1在2之前排序时,这是因为释放还是仅仅是分号?仅仅是分号。@JosephGarvin:如果2。如果也放松了,它仍然会在1之后按分号排序,但它无法与来自另一个线程的获取加载同步。i、 e.在@JosephGarvin:单独,如果为1,则您将丢失用于创建内部线程的A与X同步部分。是释放和2。如果你放松了,你还是会完蛋的。发布只是一个单向屏障,不能确保后续操作在发布之前不可见。i、 e.在一个真正的CPU上,一个发布存储可以在存储缓冲区中放置,直到稍后的加载之后,如果这些稍后的存储也没有发布,甚至存储变得全局可见。当然,C++中编译时重新排序也可以,不只是运行时,但有时它可以是一个有用的心智模型。如果不说“1”在2之前被排序,那么这是因为释放或仅仅是分号?这是分号。@ JoePavavin:如果是2。如果也放松了,它仍然会在1之后按分号排序,但它无法与来自另一个线程的获取加载同步。i、 e.在@JosephGarvin:单独,如果为1,则您将丢失用于创建内部线程的A与X同步部分。是释放和2。如果你放松了,你还是会完蛋的。发布只是一个单向屏障,不能确保后续操作在发布之前不可见。i、 e.在一个真正的CPU上,一个发布存储可以在存储缓冲区中放置,直到稍后的加载之后,如果这些稍后的存储也没有发布,甚至存储变得全局可见。当然,C++中编译时重新排序也是允许的,而不仅仅是运行时,但有时它可以是一个有用的心智模型。