彼得森的解决方案只使用一个变量



对于Pi:

do {
turn = i;   // prepare enter section
while(turn==j);
//critical section 
turn = j; //exit section. 
} while(true);

对于Pj:

do {
turn = j;   // prepare enter section
while(turn==i);
//critical section 
turn = i; //exit section. 
} while(true);

在这个简化算法中,如果过程i想要进入i的临界区间,它将设置";turn=i";(不同于Peterson的将设置"turn=j"的解(。这个算法似乎不会导致死锁或饥饿,那么为什么Peterson的算法没有这样简化呢?

另一个问题:正如我所知,信号量p/V操作等互斥机制需要原子性(p应该同时测试sem.value和sem.value-(。但是为什么上面的算法只使用一个变量turn似乎不需要原子性(turn=i,test turn=j不是原子性(?

在询问算法是否避免死锁和饥饿之前,您必须首先验证它是否仍然锁定。对于您的版本,即使假设顺序一致,操作也可以按如下顺序排列:

Pi                                         Pj
turn = i;
while (turn == j); // exits immediately
turn = j;
while (turn == i); // exits immediately
// critical section                        // critical section

你违反了锁的规定。


对于你的第二个问题:这取决于你所说的";原子性";。您确实需要这样的情况:当一个线程存储turn = i;时,另一个加载turn的线程将只读取ij,而不读取其他任何内容。在某些机器上,根据turn的类型以及ij的值,可能会撕裂并加载完全不同的值。因此,无论您使用什么语言,都可能需要将CCD_ 9声明为";原子";以某种方式避免这种情况。特别是在C++中,如果turn没有声明为std::atomic,那么任何并发读/写访问都是一场数据竞赛,整个程序的行为都会变得未定义(这很糟糕(。

除了需要避免撕裂和数据竞争外,Peterson的算法还需要严格的内存排序(顺序一致性(,除非特别要求,否则在许多系统/语言上无法保证这一点,同样可能是通过以某种方式将变量声明为atomic

的确,与更典型的锁算法不同,Peterson不需要原子读-修改-写,只需要原子顺序一致的加载和存储。这正是它成为一个有趣而聪明的算法的原因。但在复杂性和性能方面存在很大的折衷,尤其是如果您想要两个以上的线程,并且大多数现实生活中的系统都有相当高效的原子RMW指令,因此Peterson很少在实践中使用。

最新更新