Server/Study

[Server] Interlocked

GiveZero 2023. 4. 26. 11:49
using System;

namespace Server
{
    class Program
    {
        private static int number = 0;

        static void Thread1()
        {
            for (int i = 0; i < 10000; i++)
            {
                number++;
            }
        }

        static void Thread2()
        {
            for (int i = 0; i < 10000; i++)
            {
                number--;
            }
        }
        
        static void Main(string[] args)
        {
            Task t1 = new Task(Thread1);
            Task t2 = new Task(Thread2);
            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);

            Console.WriteLine(number);

        }
    }
}

위와 같이 실행하면 0이 나오는가?

나오지 않음

 

이유 - Race Condition (경합조건)

        static void Thread1()
        {
            for (int i = 0; i < 10000; i++)
            {
                number++;
            }
        }

위의 상황은 아래의 상황으로 이루어진다. (단 임시 변수가 아닌 레지스터로 이루어짐)

number++이라거나 number--와 같은 단순 덧셈 연산도
실제 바이너리 코드를 까보면 3단계에 걸쳐서
- 1) 데이터를 eax 레지스터에 갖고 오고
- 2) eax 값을 1 증가시키고
- 3) eax 값을 다시 데이터에 저장

 형태로 이루어지기 때문에 쓰레드 사이에 경합이 일어나면 문제 발생

(2단계를 호출하고 3은 아직 안 한 상태에서, 다른 쓰레드가 끼어들어 1을 실행한다거나..)

        static void Thread1()
        {
            for (int i = 0; i < 10000; i++)
            {
               	int temp = number;
                temp +=1;
                number = temp;
            }
        }

한번에 일어나야할 일이 여러 동작으로 나누어 이루어 졌기 때문

-> critical section

임계 영역

 

원자성

 

number++를 atomic하게 만드는 Interlocked을 활용

        static void Thread1()
        {
            for (int i = 0; i < 10000; i++)
            {
                // 성능의 문제가 발생 하지만 현재 상태의 해결책
                Interlocked.Increment(ref number);
            }
        }

        static void Thread2()
        {
            for (int i = 0; i < 10000; i++)
            {
                Interlocked.Decrement(ref number);
            }
        }

ref number 을 사용하는 이유

int a = number  와 같이 꺼내와 쓰면 다른 곳에서도 사용중일 수 있기 때문

그래서 Interlocked.Increment() 의 반환값은 int이고, 이것으로 검증가능