class SpinLock
{
volatile bool _locked = false;
public void Acquire()
{
while (_locked)
{
}
_locked = true;
}
public void Release()
{
_locked = false;
}
}
스핀락을 클래스로 구현하여 보았다.
_locked 라는 불리언 변수를 두고 해당 값에 따라 while문을 돌면서 대기하도록 만들었다.
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);
}
Thread1과 Thread2는 SpinLock을 활용하여 num값을 증가, 감소시키는 함수이다.
기댓값은 0이다.
결과는 위와 같다.
왜냐하면 Thread1과 Thread2가 거의 동시에 실행되어 Acquire의 while문을 둘 다 통과하는 경우가 생기기 때문이다.
public void Acquire()
{
while (true)
{
int original = Interlocked.Exchange(ref _locked, 1);
if (original == 0)
break;
}
_locked = 1;
}
Interlocked.Exchange()를 사용한다.
기존에 _locked의 값은 original로 들어가고, _locked의 값을 1로 갱신한다.
즉 _locked == 0인 경우 락을 획득하게 된다.
public void Acquire()
{
// CAS Compare-And-Swap
while (true)
{
int expected = 0;
int desired = 1;
if (Interlocked.CompareExchange(ref _locked, desired, expected) == expected)
break;
}
_locked = 1;
}
위와 같이 해결 할 수도 있다.
CompareExchange 함수를 통해, _locked의 값이 expected의 값과 같으면 해당 값을 desired 값으로 바꾼다.
그리고 반환되는 값은 기존의 _locked의 값이 되겠다.