1

我试图用 C# 像这样实现彼得森锁

public class PetersonLock
{
    private volatile bool[] flag = new bool[2];
    private volatile int victim;
    public int oneThreadId;

    public void Lock() {
        int i = Thread.CurrentThread.ManagedThreadId == oneThreadId ? 1 : 0;
        int j = 1 - i;
        flag[i] = true; /* A */
        victim = i;     /* B */
        while (flag[j] && victim == i) { } /* C */
    }

    public void Unlock() {
        int i = Thread.CurrentThread.ManagedThreadId == oneThreadId ? 1 : 0;
        flag[i] = false;
    }
}

但是当我在2个线程中使用这个锁时,它不起作用,有人说我应该考虑指令重排序并使用内存屏障。A线,B线和C线是否像A->B->C,或A->C->B,或C->A->B或其他顺序重新排序?所以我把我的代码改成这样:

public class PetersonLock
{
    private volatile bool[] flag = new bool[2];
    private volatile int victim;
    public int oneThreadId;

    public void Lock() {
        int i = Thread.CurrentThread.ManagedThreadId == oneThreadId ? 1 : 0;
        int j = 1 - i;
        flag[i] = true;
        Thread.MemoryBarrier(); // Is this line nesscessary?
        victim = i;
        Thread.MemoryBarrier(); // Is this line nesscessary?
        while (flag[j] && victim == i) { }
    }

    public void Unlock() {
        int i = Thread.CurrentThread.ManagedThreadId == oneThreadId ? 1 : 0;
        flag[i] = false;
    }
}
  • 不知道这两行是不是都需要?
  • 有什么规则可以帮助我判断哪一行会被重新排序,哪一行不会?
  • 什么时候应该使用内存屏障?
4

1 回答 1

0

根据Microsoft 文档,有更简单的方法可以使用锁和 Monitor 类来同步代码。如果你还想坚持Thread.MemoryBarrier,我发现这个链接很有趣。

关于你的代码,我对 PetersonLock 不是很熟悉,所以不要盲目相信我的话。我会说第一个屏障用于确保在设置受害者之前设置标志,并且需要第二个屏障将内存刷新到所有缓存的内存行,以便所有线程读取相同的值

于 2021-04-17T13:22:06.303 回答