* 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은 정수 변수에 대해서만 사용한다는 것을 유의하자.
'게임 서버 > Thread' 카테고리의 다른 글
[c#/유니티] DeadLock과 SpinLock에 대해 (0) | 2022.12.08 |
---|---|
[c#/유니티] 임계영역(Critical Section)을 위한 Monitor와 Lock (0) | 2022.12.07 |
[c#/유니티] 캐시(Cache) 이론과 메모리 배리어(Memory Barrier) (2) | 2022.12.06 |
[c#/유니티] 컴파일러 최적화 (1) | 2022.12.06 |
[c#/유니티] 스레드(Thread)와 스레드풀(ThreadPool) (0) | 2022.12.05 |