似乎接受和释放语义的定义是这样的:
(引自http://msdn.microsoft.com/en-us/library/windows/hardware/ff540496(v=vs.85).aspx)



我已经简要了解了一半内存障碍的存在,并且据推测,它们遵循上述相同的语义,具有获取障碍和释放障碍的味道。

在查找硬件说明的真实示例时,我遇到了SFENCE。这个博客(http://peeterjoot.wordpress.com/2009/12/04/intel-memory-ordering-fence-instructions-and-atomic-operations/)说这是一种释放篱笆/屏障的形式:



但是,阅读SFENCE的定义后,它似乎没有提供发布语义,因为它根本不与负载同步?据我所知,发布语义定义了相对于所有内存操作(加载和存储)的顺序。

最佳答案

LFENCE没有获取语义。 SFENCE没有发布语义。有一个很好的理由:拥有带有获取语义或释放语义的独立篱笆指令几乎是完全无用的。为了使获取/发布有任何好处,必须将其与内存操作绑定(bind)在一起。

例如,考虑在两个线程之间发送数据的通用习惯用法:

  • 处理器A写入缓冲区。
  • 处理器A将“true”写入标志。
  • 处理器B等待直到标志为真。
  • 处理器B读取缓冲区。

  • 请注意,处理器A必须确保在写入缓冲区后可以看到其对标志的写入。现在假设我们有一个“RFENCE”指令,它是一个释放栅栏。如果我们将指令紧跟在步骤(1)之后,那么这样做是没有用的,因为允许步骤2中的写入看起来像是通过RFENCE向上迁移,还是向上迁移至步骤1。

    类似的论点表明,执行获取操作的“AFENCE”指令对于确保步骤3中的标志读取不会跨步骤4向下迁移同样无效。

    Itanium通过提供“释放后写入”和“获取时加载”指令将栅栏绑定(bind)到内存操作,从而优雅地解决了该问题。

    返回到IA-32和Intel64:如果程序不使用“非临时”指令,则其余指令的行为就像每个加载都在执行“acquire”而每个商店都在执行“release”一样。参见Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 3A的第8.2.3节(和小节)。如果涉及“非临时性”商店,那么您可以通过以下几种方法来强制执行围栏操作:
  • 使用SFENCE
  • 使用MFENCE-有点矫_过正
  • 使用LOCK前缀的指令(例如“LOCK INC”)来写入标志。带有LOCK前缀的指令隐式具有MFENCE。
  • 使用XCHG(它好像具有隐式的LOCK前缀)来写入标志。

  • 例如,如果在较早的习惯用法中,缓冲区是使用非临时存储写入的,则让处理器A在步骤1和步骤2之间发出SFENCE或MFENCE。或者使用XCHG写入标志。

    以上所有说明均适用于硬件。使用高级语言时,请确保编译器不会破坏事件的关键顺序。存在C++ 11原子操作库,因此您可以告诉编译器和硬件您打算做什么。

    关于multithreading - 英特尔SFENCE是否具有发布语义?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16071682/

    10-13 05:07