 
 static int x = 0;
static int y = 0;
static int r1 = 0;
static int r2 = 0;
static void Thread_1()
{
    y = 1;
    r1 = x;
}
static void Thread_2()
{
    x = 1;
    r2 = y;
}
static void Main(string[] args)
{
    int count = 0;
    while(true)
    {
        count++;
        x = y = r1 = r2 = 0;
        Task t1 = new Task(Thread_1);
        Task t2 = new Task(Thread_2);
        t1.Start();
        t2.Start();
        Task.WaitAll(t1, t2);
        // 과연 빠져나오는 경우가 있을까?
        if(r1 == 0 && r2 == 0)
         break;
    }
    // 생각보다 금방 빠져나온다 (보통 3~5번만에)
    Console.WriteLine($"{count}번만에 빠져나옴");
}
이게 가능한가???
// CPU입장에서는 아래의 연산들이
static void Thread_1()
{
    // y = 1;과 r1 = x;연산의 연관성이 없기에 
    // r1 = x;를 먼저 실행하고 y = 1;을 실행하는 경우도 발생
    y = 1;
    r1 = x;
}
static void Thread_2()
{
    // 여기도 마찬가지
    x = 1;
    r2 = y;
}
// 그럼 r1, r2가 모두 0이 나올수 있다
이런식으로 CPU에서 마음대로 최적화를 해버리는데 이를 방지해 보자(->메모리 배리어)
- 코드재배치 억제
- 가시성 향상
static void Thread_1()
{
    y = 1;
    Thread.MemoryBarrier();
    // Thread.MemoryBarrier(); : Store, Load를 모두 막는다
    r1 = x;
}
static void Thread_2()
{
    x = 1;
    Thread.MemoryBarrier();
    r2 = y;
}
- 사실 Thread.MemoryBarrier()를 직접적으로 쓰게 되는 경우는 드물다
- 이런식으로 메모리 배리어를 두고, Lock등이 구현된다고 알고있자
마지막으로 예제하나만 보고 넘어가자
int _answer;
bool _complete;
void A()
{
    _answer = 123;
    Thread.MemoryBarrier(); // 여기는 왜 사용되는지 알겠고
    _complete = true;
    Thread.MemoryBarrier(); 
    // 두 번째 배리어의 경우 
    // _complete = true; 이후 Flush 메모리에 올리는 것을 진행해 달라는 요청
}
void B()
{
    // 여기 배리어의 경우
    // if문의 _complete를 읽기위해서 최신의 메모리를 읽어와 달라는 요청
    Thread.MemoryBarrier();
    if(_complete)
    {
        // 여기는 상동
        Thread.MemoryBarrier();
        Console.WriteLine(_answer);
    }
}