关于记忆障碍的使用,我有些不明白的事情,我希望澄清一下。
所以,想象一下我们有一个Treibers堆栈,但是我们正在使用SMR,所以没有与每个指针关联的计数器 - 我们必须在我们的原子操作中得到正确的指针(这不是关于ABA - 我们是使用SMR处理ABA,这不是问题的一部分。)
现在,让我们假设我们正在研究英特尔(x86 / x64),因此每个CAS都带有完整的内存屏障。我认为当CASing是高速缓存行被锁定时会发生什么,发出读屏障,清除无效队列,因此高速缓存行加载了该高速缓存行的最新版本,比较发生,然后发出写屏障,刷新存储缓冲区,最后我们释放缓存线锁。
因此,我们有以下pop代码;
BARRIER_PROCESSOR_READ;
original_top = stack_state->top;
do
{
if( original_top == NULL )
return( 0 );
copy_of_original_top = original_top;
original_top = compare_and_swap( &stack_state->top, original_top->next, original_top );
}
while( copy_of_original_top != original_top );
*user_data = original_top->user_data;
因此,我们首先发出一个读屏障 - 这可以确保我们清除无效队列。但是这样做与读取state_state-> top之间存在差距。在清除无效队列和读取state_stack-> top之间,可能发生任何事情。核心可以服务中断,有总线争用并且非常慢,你可以命名 - 无效缓存行可以重新加载(并由另一个处理器重新无效)。基本上 - 无效队列可以重新填充。这意味着我们实际上无法信任original_top的价值;我们可能正在读取一个实际上是错误的本地缓存行(我们还没有使它失效)并且通过这样做,错误地认为它的值为NULL并返回0.
所以基本上,我没有看到如何阅读障碍帮助,因为任何事情仍然可以在障碍之后但在您希望执行的实际阅读之前发生。
我在这里缺少什么?
答案 0 :(得分:1)
我不完全理解你的问题,但我强烈怀疑你错过了一个细节。
内存防护用于保证更改的可见性,而不是用于同步进程。单独屏蔽不会锁定对数据的访问。
另一方面,原子操作和锁定(如互斥锁或临界区或信号量或任何其他同步原语)将保证只有一个线程访问给定的内存区域(假设所有访问都编码为'拥有'这样的锁或原子)。但他们不保证有序可见性。
如果您需要两者,则需要两者独占访问和防护(注意:防护通常已经作为高级同步原语(如互斥锁)的一部分实现,所以如果你使用那些你不必担心明确围栏的人。)
答案 1 :(得分:0)
我不完全确定我理解你的问题,但是,在发出读取屏障后,任何后续读取肯定会在屏障之前发生的读取后进行排序。根据exactky如何定义BARRIER_PROCESS_READ,它还可能强制后续读取从共享内存而不是特定于处理器的高速缓存行中提取数据,这意味着在其他处理器上执行的写入将是可见的(假设这些写入后面是适当的写屏障!)。
在存在中断的情况下,这些事情仍然存在。即使缓存行在读取障碍之后从中断处理程序中填充,然后从这些缓存行读取仍然会给出一个对读取障碍的语义有效的值。
我怀疑在您提供的代码示例中,读屏障的目的实际上是让其他处理器的写入可见,以确保下一行 - original_top = stack_state->top;
- 检索新值而不是值在屏障之前发生的读取之后已经在本地缓存。如果中断处理程序读取相同的地址,则此约束仍然为真。读取的值不会“新鲜”,但它至少不会是在无限时间内缓存的值。