본문 바로가기
게임 서버/Thread

[c#/유니티] Interlocked에 대해

by 얘리밍 2022. 12. 7.
320x100
728x90

 

*  Interlocked란?

     int 형 값증가시키거나 감소시키는 데 사용하는 클래스 이다. 

     원자성순서를 보장해준다. (단점 : 성능에서 손해)

 

 

 

     다음과 같이 int 형 전역 변수인 number를 공유하고 있는 상황에서 

     Thread_1과 Thread_2가 number를 증가시키거나 감소시키는 작업을 하려 한다.

	static int number = 0;  //전역변수

        static void Thread_1()
        {
            for (int i = 0; i < 100000; i++)
                number++;
        }

        static void Thread_2()
        {
            for (int i = 0; i < 100000; i++)
                number--;
        }

 

결과값으로 0을 기대 했지만, 이상한 값이 나오는 것을 볼 수 있다. 

 

 

 

왜..? 🤔

 

    thread1와 thread2가 동시에 number 값을 읽어와서 thread1이 number 값을 변경한다고 해도,

    thread2에서 다시 변경하면 thread1의 변경사항은 없어지기 때문이다. 

 

 

 

 

   number++와 number--는 코드 한 줄에 작동하는 것 처럼 보이지만,

   사실은 다음과 같이 세 단계를 통해 이루어 진다. 

 

int temp = number;
temp += 1;
number = temp;

 

 

 다시 말해, 자원을 가져다 쓰는 순서가 정해져 있지 않기 때문에 

 number = 0을 가지고 있는 상황에서 증감을 먼저 할지, 감산을 할지 알 수 없어 최종 값을

 보장할 수 없는 것이다. 

 

 

 

 

📌 해결 방법

 

       이를 해결하기 위해 Interlocked를 사용한다. 

 

     * Interlocked 클래스 (System.Threading 클래스 안에 있음) 

메소드 이름
설 명
CompareExchange 두 대상을 비교하여 값이 같으면 지정된 값을 설정하고, 그렇지 않으면 연산을 수행하지 않는다.
Decrement 지정된 변수의 값을 감소시키고 저장한다.
Exchange 변수를 지정된 값으로 설정한다.
Increment 지정된 변수의 값을 증가시키고 저장한다.

 

 

   이 때, 주의할 점이 값 그 자체를 전달하는 것이 아니라,

   ref를 통해 주소값을 참조하여 전달한다는 것이다. 

 

 

    그래야 다른 곳에서 이루어진 변경 작업에 대해 값을 업데이트 해 가져올 수 있다. 

 

 

 	static int number = 0;

        static void Thread_1()
        {
            for (int i = 0; i < 100000; i++)
                //ref로 넣어줌 -> 숫자 자체가 아니라 주소값을 넣어줌 
                Interlocked.Increment(ref number); 
                
        }

        static void Thread_2()
        {
            for (int i = 0; i < 100000; i++)
                Interlocked.Decrement(ref number);  
        }
           
        static void Main(string[] args)
        {
            
            Task t1 = new Task(Thread_1);
            Task t2 = new Task(Thread_2);
            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);

            Console.WriteLine(number);
        }
    }

 

 

  이제 코드를 실행하면,

 

 

  올바른 값이 출력됨을 볼 수 있다. 

  Interlock정수 변수에 대해서만 사용한다는 것을 유의하자. 

 

 

 

 

출처 : https://www.inflearn.com/course/유니티-mmorpg-개발-part4

728x90
반응형