Lock을 누가 점유하고 있다면?
1) 무작정 기다린다. => SpinLock
2) 나중에 다시 시도한다. (랜덤성) => 쓰레드가 자기 소유권을 포기 Context Switching
3) 운영체제의 도움을 받는다. AutoResetEvent
lock을 획득할 때까지 무한정 try를 한다.
Interlocked.Exchange 키워드를 이용해서 _locked에 1이란 값을 넣어준다.
여기서 original 변수는 stack에 있는 변수이므로 경합 현상은 생각하지 않는다.
using System;
using System.Threading;
namespace SeverCore
{
class SpinLock
{
volatile int _locked = 0;
public void Acquire()
{
while (true)
{
// original <- _locked에 1을 넣어주기 이전 값
int original = Interlocked.Exchange(ref _locked, 1);
if (original == 0)
break;
}
}
public void Release()
{
_locked = 0;
}
}
class Program
{
static int _num = 0;
static SpinLock _lock = new SpinLock();
static void Thread_1()
{
for (int i = 0; i < 100000; i++)
{
_lock.Acquire();
_num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 100000; 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);
}
}
}
Interlocked.Exchange 부분을 자세히 보자.
int original = Interlocked.Exchange(ref _locked, 1);
if (original == 0)
break;
즉, 위 코드는 아래와 같이 해석될 수 있다.
int original = _locked;
_locked = 1
if (original == 0)
break;
또는 아래와 같이 Interlocked.CompareExchange 키워드를 사용할 수도 있다.
int original =Interlocked.CompareExchange(ref _locked, 1, 0);
if (original == 0)
break;
C++ 형식으로 변환하면 아래와 같다.
int expected = 0;
int desired = 1;
if(Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
break;
Acquire()에서 처리를 해주었기 때문에 Release()는 그대로 진행해도 된다.
using System;
using System.Threading;
namespace SeverCore
{
class SpinLock
{
volatile int _locked = 0;
public void Acquire()
{
while (true)
{
int expected = 0;
int desired = 1;
if(Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
break;
}
}
public void Release()
{
_locked = 0;
}
}
class Program
{
static int _num = 0;
static SpinLock _lock = new SpinLock();
static void Thread_1()
{
for (int i = 0; i < 100000; i++)
{
_lock.Acquire();
_num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 100000; 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);
}
}
}