SpinLock(스핀락)
동기화 기법 중 하나로 여러 쓰레드가 공유자원에 접근한느 것을 제어하기 위해 사용된다. race condition을 해결하고 mutal exclusion을 구현하기 위해 이용된다.
스핀락 구현
class SpinLock
{
// true이면 누가 화장실 사용중, false이면 빈 화장실
// volatile 가시성을 보장
volatile bool _locked = false;
// 열쇠 획득 함수
public void Acquire()
{
// 잠금이 풀릴때까지 계속 뺑뺑 돈다
while(_locked)
{
// 잠금이 풀릴때까지 기다린다
}
// 이제 내가 들어간다
_locked = true;
}
// 열쇠 반납 함수
public void Release()
{
// 나 나간다
_locked = false;
}
}
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();
}
}
}
결과가 왜 0이 안나올까?
화장실로 비교해보면 만약 두사람이 있으면 서로 먼저 들어갈려고 경합을 버릴 것이다.
정말 드물게 일어나긴 하지만 애시당초 화장실에 사람이 없기 때문에 동시에 들어가는 경우가 생길 수 있다.
어쨌든 화장실에 들어가는 것이 목표였기 때문에 그 다음에는 문을 잠가버리게 된다.
결과가 0이 나오게 하는 방법
문안에 들어간 다음에 문을 잠그는 것이 하나의 행동으로 이루어져야지 2가지 동작( 1. 문을 들어간다 2. 문을 잠근다)로 이루어지면 안된다.원자성을 얘기했을때 이러한 이야기를 한적이 있다.
결국에는 동시에 들어가는 상황을 원천적으로 차단해야 한다.
문제가 되는 부분
이 부분이 이렇게 2가지 동작으로 동작하는데 이를 한번에 동작하게 수정해야 한다.
// 잠금이 풀릴때까지 계속 뺑뺑 돈다
while(_locked)
{
// 잠금이 풀릴때까지 기다린다
}
// 이제 내가 들어간다
_locked = true;
해결 방법
Interlocked 알아보러 가기
01 Test - and - Set 스핀락
Test - and - Set 연산은 SpinLock변수의 값을 읽고, 새로운 값을 설정하는 원자적인 연산이다.
volatile int _locked = 0;
while (true)
{
int original = Interlocked.Exchange(ref _locked, 1);
if (original == 0)
break;
}
_locked 초기값이 0인경우 Interlocked.Exchange를 호출하면 _locked값은 1로 변경된다.
Interlocked.Exchange의 반환 값은 _locked원래 값이므로 0이 반환된다.
-----------------------------------------------------------------------------------_locked = 1, original = 0
싱글 쓰레드인 경우
위의 코드를 아래와 같이 풀어쓸 수 있다.
while(true)
{
int original = _locked;
_locked = 1;
if(original == 0)
break;
}
_locked 공유해서 경합해서 사용하는 아이이기 때문에 우리 멋대로 값을 읽어서 사용하면 안되는데 왜 사용했나요?
싱글 쓰레드 환경에서 original은 stack에 있는 경합하지 않는 하나의 쓰레드에서 사용하므로 상관이 없다.
이렇게 하면 코드가 더 깔끔할 것 같은데 이런 방법은 없을까?
if(_locked == 0)
_loced == 1;
02 Compare - And - Swap 스핀락
compare and swap 연산은 변수의 현재 값을 읽고 기대하는 값과 비교한 후 값이 일치하면 새로운 값으로 설정한다.
int original = Interlocked.CompareExchange(ref _locked, 1, 0);
if (original == 0)
break;
_locked 초가깂이 0인 경우 CompareExchange를 호출하게 되면 _locked값과 0값이 같으므로 _locked값이 1로 변경된다. 반환값은 _locked원래 값이므로 0이 반환된다.
-----------------------------------------------------------------------------------_locked = 1, original = 0
헷갈릴수도 있으니까 변수에 이름을 붙이자
int expected = 0;
int desire = 1;
int original = Interlocked.CompareExchange(ref _locked, desire, expected);
if (original == expected)
break;
결과
참고 : 본 내용은 MMORPG PART4 강의를 수강하여 작성하였습니다.
https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4
'cs공부 > 운영체제' 카테고리의 다른 글
운영체제 - Backoff(Thread.Sleep(0), Thread.Yield()) (0) | 2023.05.26 |
---|---|
운영체제 - Semaphore(세마포어), Mutex(뮤텍스) (0) | 2023.05.23 |
운영체제 - C# Lock구현 연습 (0) | 2023.05.22 |
운영체제 - DeadLock(데드락) (0) | 2023.05.22 |
운영체제 - Peterson's Solution, Bakery 알고리즘(상호배제 문제 해결) (0) | 2023.05.16 |
댓글