Server/Study
[Server] MemoryBarrier
GiveZero
2023. 4. 25. 23:37
예시
using System;
namespace Server
{
class Program
{
private static int x = 0;
private static int y = 0;
private static int r1 = 0;
private static int r2 = 0;
static void Thread1()
{
y = 1;
r1 = x;
}
static void Thread2()
{
x = 1;
r2 = y;
}
static void Main(string[] args)
{
int count = 0;
while (true)
{
count++;
x = y = r1 = r2 = 0;
Task t1 = new Task(Thread1);
Task t2 = new Task(Thread2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
if (r1 == 0 && r2 == 0)
break;
}
Console.WriteLine($"{count}만에 break");
}
}
}
위와같이 진행하면
결국엔 break 되어 출력된다.
왜..?
Thread 1 , Thread 2를 어떻게 조합하더라도 0이 나올 수 있는 경우의수 X
하드웨어가 최적화 일으키며 발생하는 문제
=> 의존성이 없는 명령어라고 판단했을 경우 순서가 뒤바뀔 수 있음
즉, Y=1, R1 = X 의 연관성은 없기 때문에 뒤집어 질 수 있음.
Y=1 → R1 = X 순이 아닌, R1 = X → Y = 1 로 진행될 수 있음
이와 같은 방식은
싱글 스레드에서는 전혀 문제가 되지 않지만, 멀티 스레드에선 문제가 될 수 있음.
해결방법
Memory Barrier
- 코드 재배치 억제
- 가시성
1. Full Memory Barrier (ASM MFENCE, C# Thread.MemoryBarrier) : Store , Load
2. Store Memory Barrier(ASM SFENCE) : Store
3. Load Memory Barrier(ASM LFENCE) : Load
using System;
namespace Server
{
class Program
{
private static int x = 0;
private static int y = 0;
private static int r1 = 0;
private static int r2 = 0;
static void Thread1()
{
y = 1; // Store
Thread.MemoryBarrier();
r1 = x; // Load
}
static void Thread2()
{
x = 1;
Thread.MemoryBarrier();
r2 = y;
}
static void Main(string[] args)
{
int count = 0;
while (true)
{
count++;
x = y = r1 = r2 = 0;
Task t1 = new Task(Thread1);
Task t2 = new Task(Thread2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
if (r1 == 0 && r2 == 0)
break;
}
Console.WriteLine($"{count}만에 break");
}
}
}