多线程编程指南

“共享内存”多处理器

请考虑示例 9–5 中显示的对生成方和使用者问题的假设解决方案。

尽管此程序是在当前基于 SPARC 的多处理器上工作,但该程序假设所有的多处理器都有强秩序存储器 (strongly ordered memory)。因此,此程序不是可移植的。


示例 9–5 生成方和使用者问题:共享内存多处理器

                    char buffer[BSIZE];

                    unsigned int in = 0;

                    unsigned int out = 0;



void                             char

producer(char item) {              consumer(void) {

                                    char item;

    do                               

        ;/* nothing */                do  

    while                                 ;/* nothing */

        (in - out == BSIZE);           while

                                         (in - out == 0);

    buffer[in%BSIZE] = item;            item = buffer[out%BSIZE];

    in++;                             out++;

}                                }

当此程序确实具有一个生成方和一个使用者,而且在共享内存多处理器上运行时,该程序看上去是正确的。inout 之间的差异就是缓冲区中的项数目。

生成方通过重复计算此差异一直等待,直到缓冲区中有可用空间来存放新项为止。使用者一直等到缓冲区中存在项为止。

严格排序的内存可以对一个处理器上可供其他处理器直接使用的内存进行修改。对于强秩序存储器,该解决方案是正确的,即使考虑到 inout 最终会溢出也是如此。只要 BSIZE 小于以单个词表示的最大整数,就会发生溢出。

共享内存多处理器不一定具有强秩序存储器。由一个处理器对内存所做的更改不一定可直接用于其他处理器。请了解一个处理器对不同内存位置进行两次更改时发生的具体情况。其他处理器不一定会按照更改顺序检测更改,因为不会立即修改内存。

首先,会将更改存储在对于高速缓存不可见的存储缓冲区中。

处理器将检查这些存储缓冲区,以确保程序具有一致的视图。但是,由于存储缓冲区对于其他处理器是不可见的,因此在某个处理器写入高速缓存之前,该处理器写入的内容不可见。

同步元语使用刷新存储缓冲区的特殊指令来执行高速缓存。请参见第 4 章,用同步对象编程。因此,对共享数据使用锁定可确保内存的一致性。

如果内存排序非常宽松,则示例 9–5 存在问题。使用者可能发现,在其查看对相应缓冲槽位所做的更改之前生成方已经增大了 in

此排序称为弱排序,因为由一个处理器执行的存储在由另一个处理器执行时顺序可能会被打乱。但是,内存在同一个处理器中始终是一致的。要解决此不一致性,代码应使用互斥来刷新高速缓存。

由于趋势朝着放宽内存顺序方向发展,因此程序员在对所有的全局数据或共享数据使用锁定时变得越来越谨慎。

示例 9–5示例 9–6 所示,锁定是最基本的。