공부/게임서버
Monitor 사용 시 주의사항
samosa
2024. 11. 13. 23:16
number++
와 number--
연산이 각각 다른 함수에서 실행될 때, 이 코드가 다중 스레드 환경에서 경합 조건(race condition)을 일으킬 수 있는지, 그리고 이를 상호배제(Mutual Exclusion)로 해결하는 방법을 예시로 설명하겠다.
문제 상황: 경합 조건 발생
다음은 두 개의 함수가 공유 자원 number
에 동시에 접근하는 예시이다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RaceConditionExample
{
internal class Program
{
private static int number = 0;
private static void Increment()
{
for (int i = 0; i < 1000000; i++)
{
number++; // 원자적이지 않은 연산
}
}
private static void Decrement()
{
for (int i = 0; i < 1000000; i++)
{
number--; // 원자적이지 않은 연산
}
}
private static void Main(string[] args)
{
var t1 = new Task(Increment);
var t2 = new Task(Decrement);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine($"Final value of number: {number}");
}
}
}
문제 설명
number++
와number--
는 각각 메모리에서 값을 읽고, 수정하고, 다시 저장하는 과정으로 이루어진다. 이 과정은 여러 단계로 이루어져 있어 원자적이지 않다.- 두 스레드가 동시에
number
에 접근하면 예기치 않은 결과가 발생할 수 있다. 예를 들어,t1
이number++
를 수행하는 중에t2
가number--
를 수행하면 중간 값이 손실될 수 있다.
해결 방법: 상호배제를 통한 임계영역 보호
Monitor
클래스를 사용하여 number
변수에 대한 접근을 보호하는 코드를 작성할 수 있다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CriticalSectionExample
{
internal class Program
{
private static int number = 0;
private static readonly object _lockObject = new object();
private static void Increment()
{
for (int i = 0; i < 1000000; i++)
{
Monitor.Enter(_lockObject); // 상호배제를 위한 잠금
try
{
number++;
}
finally
{
Monitor.Exit(_lockObject); // 잠금 해제
}
}
}
private static void Decrement()
{
for (int i = 0; i < 1000000; i++)
{
Monitor.Enter(_lockObject); // 상호배제를 위한 잠금
try
{
number--;
}
finally
{
Monitor.Exit(_lockObject); // 잠금 해제
}
}
}
private static void Main(string[] args)
{
var t1 = new Task(Increment);
var t2 = new Task(Decrement);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine($"Final value of number: {number}");
}
}
}
설명
Monitor.Enter(_lockObject)
와Monitor.Exit(_lockObject)
를 사용하여number
변수에 대한 접근을 보호한다.try-finally
블록을 사용하여Monitor.Exit
이 항상 호출되도록 보장한다. 예외가 발생하더라도 잠금이 해제되어 다른 스레드가 접근할 수 있게 한다.- 이 코드를 실행하면
Increment
와Decrement
함수가 동시에 실행되더라도,number
변수에 대한 접근이 상호배제를 통해 보호되어 올바른 값이 유지된다.
이 방식으로 number
변수에 대한 동시 접근이 안전하게 이루어져 경합 조건을 방지할 수 있다.
그러나 사실상 lock을 쓰지, Monitor를 쓰지는 않는다고 함.