【问题标题】:When should I think about Memory Barrier and Instruction Reorder?我什么时候应该考虑内存屏障和指令重排序?
【发布时间】:2021-04-17 11:10:49
【问题描述】:

我尝试像这样用 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;
    }
}
  • 不知道这两行是否都需要?
  • 是否有任何规则可以帮助我判断哪一行将被重新排序,哪一行 行不会?
  • 什么时候应该使用内存屏障?

【问题讨论】:

  • “但是当我在 2 个线程中使用这个锁时,它不起作用” - “不起作用”是什么意思?您是否尝试在两个线程中使用此锁,但两个线程同时进入临界区?或者是什么?请出示您的“测试”代码。

标签: c# mutex lock-free memory-barriers instruction-reordering


【解决方案1】:

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

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

【讨论】:

  • 谢谢。实际上我测试了这个锁十亿次,如果我没有使用任何内存屏障,锁就会失败;如果我只添加第一个屏障,锁也会失败;但是,如果我只添加第二个屏障或两个屏障,锁就可以了!
  • 也许您的算法不需要在受害者保留之前激活标志。在这种情况下,不需要第一个屏障,而第二个屏障足以确保在循环开始之前在所有缓存段中更新受害者和标志。我试图想象如果受害者被设置在旗帜之前可能会发生的竞争条件,但我现在什么都没有想到。
  • @WardenAllen:请记住,没有发现问题的测试并不能证明源代码的正确性。可移植语言标准,尤其是在多线程中。很好的证据表明最终运行的机器代码在该机器上可能是安全的,但仅此而已。在 x86-64 上进行测试意味着只有编译时重新排序对很多事情很重要preshing.com/20120930/weak-vs-strong-memory-models(StoreLoad 重新排序/存储转发除外,这实际上与 Peterson 锁定相关;它确实需要在某个时候阻止 StoreLoad 重新排序。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 2011-07-03
  • 1970-01-01
  • 2012-02-02
  • 2010-10-29
  • 2021-07-09
相关资源
最近更新 更多