키를 가진 사람은 하나이고 화장실에 들어가서 문을 잠근다. 볼일을 보고 나가면서 키를 두고 나간다.
다음 코드는 SpinLock이 제대로 동작하지 않는 코드이다.
class SpinLock
{
volatile bool _locked = false;
public void Acquire()
{
while (_locked)
{
// 잠김이 풀릴 때까지 기다린다.
}
_locked = true;
}
public void Release()
{
_locked = false;
}
}
class ThreadProgram
{
static int _num = 0;
static SpinLock _lock = new SpinLock();
static void Thread_1()
{
for (int i = 0; i < 10000; ++i)
{
_lock.Acquire();
_num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 10000; ++i)
{
_lock.Acquire();
_num--;
_lock.Release();
}
}
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("num = " + _num);
}
}
원인은 여러 쓰레드에서 동시에 키(_locked)에 접근할 수 있기 때문이다.
즉, 키를 얻는 행위와 다른 사람이 들어올 수 없도록 잠그는 행위가 원자적으로 이뤄져야 한다.
이러한 처리를 위해 Interlocked 함수를 이용한다.
SpinLock 클래스를 수정한다.
class SpinLock
{
volatile int _locked = 0;
public void Acquire()
{
while (true)
{
// Version 1
// 하나의 쓰레드에서 처리하기 때문에 original 값을 비교해서 사용해도 된다.
//int original = Interlocked.Exchange(ref _locked, 1);
//if (original == 0)
// break;
// Version 2
// locked와 0의 값이 같은 경우 1로 교체하고 original 반환
int original = Interlocked.CompareExchange(ref _locked, 1, 0);
if (original == 0)
break;
//Thread.Sleep(1); // 무조건 휴식 ==> 무조건 1ms 정도 쉬고 싶어요.
//Thread.Sleep(0); // 조건부 양보 => 우선순위가 같거나 높은 스레드가 없는 경우 자신이 처리, 우선 순위가 낮은 스레드에게 양보X
Thread.Yield(); // 관대한 양보 => 실행 가능한 쓰레드가 없는 경우 자신에게 시간 부여
//C++ Style
//int expected = 0;
//int desired = 1;
//if (Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
// break;
//{
// // 싱글 스레드라면 이런 느낌
// int original = _locked;
// _locked = 1;
// if (original == 0)
// break;
//}
}
}
public void Release()
{
_locked = 0;
}
}
Interlocked 함수는 original 값을 받고 원하는 값을 연산해주는 처리를 '원자적'으로 지원한다.
original 값이 0이라면 키를 얻었다는 의미이므로 loop를 나가면 된다.
정상적으로 num의 값이 0으로 나온다.
'프로그래밍 > Game Dev' 카테고리의 다른 글
[C#] Thread Local Storage (0) | 2023.08.28 |
---|---|
[C#] AutoResetEvent (0) | 2023.08.26 |
[C#] Interlocked (0) | 2023.08.26 |
[C#] MessagePack (0) | 2023.08.26 |
[C#] 메모리 베리어 (0) | 2023.08.26 |
댓글