Когда мне следует подумать о барьере памяти и переупорядочении инструкций?

Я пытался реализовать Peterson Lock с 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;
    }
}
  • Я не знаю, нужны ли обе эти две строчки?
  • Есть ли какие-то правила, которые помогут мне решить, какая строка БУДЕТ переупорядочена, а какая - НЕ?
  • Когда мне следует использовать MemoryBarrier?

1 ответ

Согласно документации Microsoft , есть более простые способы синхронизации кода с помощью блокировок и класса Monitor. Если ты все еще хочешь придерживаться Thread.MemoryBarrierЯ нашел эту ссылку довольно интересной.

Что касается вашего кода, я не очень хорошо знаком с PetersonLock, поэтому не принимайте мои слова вслепую. Я бы сказал, что первый барьер используется для обеспечения установки флага до установки жертвы, а второй барьер необходим для сброса памяти во все кэшированные строки памяти, чтобы все потоки читали одно и то же значение.

Другие вопросы по тегам