[C# 서버] 메모리 배리어

Yerin·2023년 9월 19일

C# 게임 서버

목록 보기
4/13

아래 코드를 실행해보면 쓰레드를 무리없이 빠져나오는 것을 볼 수 있다.

namespace SeverCore
{
    class Program
    {

        static int x = 0;
        static int y = 0;
        static int r1 = 0;
        static int r2 = 0;

        static void Thread_1()
        {
            y = 1; // Store y
            r1 = x; // Load x
        }


        static void Thread_2()
        {
            x = 1; // Store x
            r1 = y; // Load 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;
            }

            Console.WriteLine( $"{count}번만에 빠져나옴!");
        }
    }
}

어떻게 이러한 일이 가능한 것일까?

위 코드가 아래와 같이 맘대로 변경이 된다는 것이다.

x = 0, y = 0
r1 = x r2 = y
y = 1 x = 1

그렇다면 해결방법은 무엇일까?

메모리 배리어

메모리 배리어의 기능은 다음과 같다.

A) 코드 재배치 억제
B) 가시성

이제 코드를 재배치 하지 않도록 아래와 같이 코드를 수정해준다.
또한 각자의 변수가 최신의 값을 가질 수 있도록 하는 역할도 한다.
Store를 한 뒤 -> 메모리 배리어를 사용
메모리 배리어를 사용한 뒤 -> Load를 함

volatile 키워드를 이용해 가시성을 해결할 수도 있다.

namespace SeverCore
{
    class Program
    {

        static volatile int x = 0;
        static volatileint y = 0;
        static volatile int r1 = 0;
        static volatile int r2 = 0;

        static void Thread_1()
        {
            y = 1; // Store y
            //-------------------------
            Thread.MemoryBarrier();
            r1 = x; // Load x
        }


        static void Thread_2()
        {
            x = 1; // Store x
            //-------------------------
            Thread.MemoryBarrier();
            r1 = y; // Load 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;
            }

            Console.WriteLine( $"{count}번만에 빠져나옴!");
        }
    }
}

메모리 배리어에도 종류가 여러가지가 있다.

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만 막는다.

profile
재밌는 코딩 공부

0개의 댓글