(C# : Server) 5. 메모리 배리어

Posted by : at

Category : Charp-Server


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);
    }
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links