多核应用编程实战1.4.2 确保内存操作的正确顺序_多核应用编程实战1.4.2 确保内存操作的正确顺序试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > 多核应用编程实战 > 1.4.2 确保内存操作的正确顺序

多核应用编程实战——1.4.2 确保内存操作的正确顺序

当系统包含多个处理器或多个核心时,还有一个需要讨论的问题:内存排序。内存排序是内存操作对于系统中其他处理器可见的顺序。大部分时间,处理器无需程序员的任何干预即能正确处理事务。 然而,有些情况下程序员确实需要介入。这些情况可能与具体的架构(SPARC处理器和x86处理器有不同的要求)或具体的实施(不同类型的SPARC处理器可能有不同的需求)有关。好消息是由于系统库实施合理的机制,使用系统库的多线程应用程序不会遇到此类问题。 另一方面,调用系统库有一些开销,因此很可能出于性能方面的动机而编写自定义的同步代码。这种情况将在第8章中阐述。 内存排序指令在SPARC上称为内存屏障(membar),在x86上称为内存栅栏(mfence)。这些指令确保了内存操作不会以错误的顺序在线程以外可见。下面的例子将说明这一点的重要性。 假设有一个经锁定机制保护的变量count,你要增加该变量的值。锁的工作原理是,当其值为1时获得锁,当其值为0时释放锁。获取锁的代码与本例无关,因此假定本例开始时已经获得锁,因此变量lock的值为1。既然已经获得锁,代码就可以增加变量count的值。其后,为了释放锁,代码将值0存储到变量lock中。递增变量以及将值0存储到lock的过程看起来有点像伪码,如代码清单1-6所示。 代码清单1-6 增加一个变量的值并释放锁 LOAD [&count], %A INC %A STORE %A, [&count] STORE 0, [&lock] 一旦值0存储到变量lock,另一个线程就可获得锁并修改变量count。出于性能方面的考虑,一些处理器实行内存操作的弱排序,也就是说,某个存储操作可移动到其他存储操作之前,而某个加载操作也可移动到其他加载操作之前。如果前面的代码在实施弱排序的机器上运行,那么执行时的代码可能如代码清单1-7所示。 代码清单1-7 弱内存排序下增加变量值并释放锁 LOAD [&count], %A INC %A STORE 0, [&lock] STORE %A, [&count] 在运行时,处理器已将存储到锁的操作提前,使之在把值存储到变量count之前就对系统的其余部分可见。因此,在count的新值可见之前锁就被释放了。另一个处理器会看到该锁是空闲的,并会加载count的旧值而非新值。 解决办法就是在两次存储操作之间加上内存屏障,告诉处理器不要对其重新排列。代码清单1-8显示了更正后的代码。在这个例子中,membar指令确保前一个存储操作在下一条存储指令执行之前完成。 代码清单1-8 使用内存屏障强制实现存储顺序 LOAD [&count], %A INC %A STORE %A, [&count] MEMBAR #store, #store STORE 0, [&lock] 其他类型的内存屏障会强制实现加载和存储操作的其他排序方式。如果没有这些内存屏障,可能会发生其他排序错误。例如,当获得锁时可能会发生同样的问题。提取count值的加载指令可能在设定获取锁的存储指令之前执行。在这种情况下,在从内存取回值到获取锁这段时间里,另一个处理器可能会修改count的值。 每个处理器系列的程序员参考手册都会详细阐述何时需要、何时不需要内存屏障的确切情况,因此在编写自定义锁代码时应参考这些文件。

展开全文


推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《多核应用编程实战》其他试读目录

• 第一章:硬件、进程和线程
• 1.1 计算机的内部结构
• 1.2 多核处理器的缘起
• 1.2.1 在单芯片上支持多线程
• 1.2.2 通过处理器核心流水线作业提高指令发出率
• 1.2.3 使用缓存保存最近使用的数据
• 1.2.4 用虚拟内存存储数据
• 1.2.5 从虚拟地址转换到物理地址
• 1.3 多处理器系统的特征
• 1.4 源代码到汇编语言的转换
• 1.4.1 32位与64位代码的性能
• 1.4.2 确保内存操作的正确顺序 [当前]
• 1.4.3 进程和线程的差异
• 1.5 小结
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  •