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