Когда мне следует подумать о барьере памяти и переупорядочении инструкций?
Я пытался реализовать 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, поэтому не принимайте мои слова вслепую. Я бы сказал, что первый барьер используется для обеспечения установки флага до установки жертвы, а второй барьер необходим для сброса памяти во все кэшированные строки памяти, чтобы все потоки читали одно и то же значение.