C#/네트워크

[C#][서버] Interlocked

goliot 2024. 6. 1. 17:41
반응형

문제 상황

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

static void Thread_2()
{
    for (int i = 0; i < 100000; i++)
        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);
}
  • 다음을 실행하면, 10만번 더하고, 10만번 뺐으니까 0이 출력돼야겠지만?

  • 이렇게 이상한 값이 출력된다.

이유

  • 우선 ++, -- 연산은 내부적으로 다음과 같이 처리된다.
int temp = number; // 0
temp += 1; // 1
number = temp; // number = 1

int temp = number; // 0  
temp -= 1; // -1  
number = temp; //number = -1;
  • 이렇게 세 줄이 모두 실행되기 전에 다른 쓰레드에서 number를 수정해 버린다면?
    • number = 1 -> number = -1 로 점프해버리는 상황이 발생한다.
  • 게임에서 예시를 들면
    1. 돈 -= 100
    2. 서버 다운 -> 이러면 돈만 먹고 아이템이 들어오지 않는 상황이 발생한다.
    3. 인벤토리 += 아이템

Race Condition

  • 열정이 넘치는 세 직원이 있고, 주문을 자기가 처리하려고 안달이 나있다.
  • 한 테이블에서 콜라 하나 주문이 들어왔다.
  • 세 직원이 모두 각자 콜라를 하나씩 들고 그 테이블로 전달했다.
  • 이런 상황이 발생하는 것이 Race Condition

InterLock

  • 이것을 해결하기 위해 ++, -- 대신 이것을 사용해보자
Interlocked.Increment(ref number);  
Interlocked.Decrement(ref number);  
//ref는 c언어의 포인터처럼 직접 메모리에 접근해 작업을 수행한다는 의미로 보면 된다,
  • 이러면 All or Nothing으로 원자적으로 모든 연산이 끝날 때 까지 number의 소유권을 갖게 되는 것이다.
  • Race Condition 문제를 해결

연산이 끝난 값을 그대로 가져오고 싶다면?

int after = number;  
Interlocked.Increment(ref number);  
after = number;
  • 이렇게 하면 2 -> 3번째 줄로 넘어가는 사이에 number가 바뀔 수 있다.
    • 대신 Increment 함수에는 리턴값이 존재하여 다음과같이 사용하면 된다.
int after = Interlocked.Increment(ref number);
  • 이렇게 하면 연산이 끝난 값을 그대로 가져오게 된다.
반응형

'C# > 네트워크' 카테고리의 다른 글

[c#][서버] 데드락  (0) 2024.06.02
[C#][서버] Lock 기초  (0) 2024.06.02
[C#][서버] 메모리 배리어  (0) 2024.06.01
[C#][서버] 캐시 이론  (0) 2024.05.31
[C#][서버] 기본 멀티쓰레드 프로그래밍  (0) 2024.05.30