1

所以,我阅读了很多关于指令和内存重新排序以及我们如何防止它的内容,但我仍然没有回答一个问题(可能是因为我不够专心)。我的问题是:我们是否保证任何原子写入都会立即将原子变量的新值存储在主存储器中?我们来看一个小例子:

std::atomic<bool> x;
std::atomic<bool> y;
std::atomic<int> count;
void WritingValues()
{
   x.store(true, std::memory_order_relaxed);
   y.store(true, std::memory_order_relaxed);
}
void ReadValues()
{
   while( !y.load(std::memory_order_relaxed) );
   if( x.load(std::memory_order_relaxed) )
       ++count;
}
int main()
{
   x = false;
   y = false;
   count = 0;
   std::thread tA(WritingValues);
   std::thread tB(ReadValues);
   tA.join();
   tB.join();
   assert( count.load() != 0 );
}

所以,在这里我们的断言肯定可以触发,因为我们使用std::memory_order_relaxed并且不阻止任何指令重新排序(或编译时的内存重新排序,我想这是同一件事)。但是如果我们在 WritingValues 中设置一些编译器屏障来防止指令重新排序,一切都会好吗?我的意思是,x.store(true, std::memory_order_relaxed) 是否保证该特定原子变量的写入将直接进入内存,没有任何延迟?还是x.load(std::memory_order_relaxed)保证该值将从内存中读取,而不是从具有无效值的缓存中读取?也就是说,本店只保证操作的原子性并具有与通常的非原子变量相同的内存行为,或者它也对内存行为有影响?

4

2 回答 2

4
 I mean, does x.store(true, std::memory_order_relaxed) guarantees, that the  
 of that particular atomic variable will be directly into the memory,  
 without any latency?  

不,它没有,事实上,如果你只读取一次布尔值和内存顺序,则没有“无效”值,真假都可以。
由于宽松的内存顺序明确保留,因此不执行任何排序。基本上,在您的情况下,它仅意味着在从 false 翻转为 true 后,在某些时候它将对所有其他进程变为 true,但并未说明“何时”会发生。所以在这里你唯一可以确定的是,它在变为真实后不会再次变为虚假。但是对于在另一个线程中它将为假多长时间没有限制。
它还保证您不会在另一个线程中看到任何部分写入的变量,但布尔值几乎不是这种情况。
您需要在这里使用 aquire 和 release。即使这样也不能保证实际内存本身,只能保证程序的行为,即使没有将数据反弹到内存中,缓存同步也可以做到这一点。

于 2019-08-09T14:38:00.870 回答
0

由于所有的加载和存储指令都是原子的,它们每个都是一条机器指令,因此两个线程永远不会在加载或存储指令的“中间”“相互中断”。

您的问题的标题是“我们是否保证任何原子写入都会立即将原子变量的新值存储在主存储器中?”。但是原子指令的定义是它不能被上下文切换、硬件中断、软件期望中断——什么都没有!

std::memory_order_relaxed允许在单个函数中对指令进行一些重新排序。例如看这个问题。这与您的问题几乎相同,但您有memory_order_relaxedinReadValues()而不是memory_order_acquire. 在此函数中,由于宽松条件(编译器重新排序等),变量上的自旋锁可能y放置在计数器增量之后。WriteValues()在任何情况下,ASSERT 都可能失败,因为由于 memory_order_relaxed 允许的内存重新排序,y 可能在 x 出现之前设置为 true (参考类似问题中的答案

于 2019-08-09T14:48:14.083 回答