KoreanFoodie's Study
[C++ 게임 서버] 1-7. SpinLock 개념과 구현 본문
[C++ 게임 서버] 1-7. SpinLock
핵심 :
1. SpinLock 은 Lock 을 잡기 위해 '무작정 기다리는' 방식이다.
2. SpinLock 은 atomic 변수와 lock, unlock 함수로 쉽게 만들 수 있다. compare_exchange_strong 함수를 기억하자.
3. SpinLock 은 불필요한 CPU clock 을 낭비하지만, 때로는 컨텍스트 스위칭에 드는 것보다 비용이 저렴할 수도 있다.
이제 본격적으로 SpinLock 을 구현해 보자.
SpinLock 은 간단하다. 그냥 Lock 을 잡을 때까지 무작정 While 문을 돌면서 기다리도록 만들 것이다 🤣
간단하게 생각하면 아래와 같이 구현하겠지만...
int sum = 0;
class SpinLock
{
private:
bool _lock = false;
public:
void lock()
{
while (_lock)
{
}
_lock = true;
}
void unlock()
{
_lock = false;
}
};
SpinLock spinLock;
void Add()
{
for (int i = 0; i < 1000; ++i)
{
spinLock.lock();
++sum;
spinLock.unlock();
}
}
void Sub()
{
for (int i = 0; i < 1000; ++i)
{
spinLock.lock();
--sum;
spinLock.unlock();
}
}
int main()
{
thread t1(Add);
thread t2(Sub);
t1.join();
t2.join();
cout << "sum : " << sum << endl;
}
위 코드는 제대로 동작하지 않는다.
그 이유는 크게 2 가지가 있다.
일단, SpinLock 클래스의 _locked 는 멀티쓰레드 환경을 고려하지 않고 선언한 녀석이다.
무슨 말인고 하니, 다음과 같은 상황이 있다고 가정해 보자.
int a = 0;
a = 1;
a = 2;
a = 3;
컴파일러는 이 코드를 어셈블리어로 만들 때, 매우 '똑똑한' 최적화를 한다. 즉, a 의 최종값은 어차피 3이니, 굳이 0, 1, 2 값을 대입하지 않고 바로 3 을 넣는 어셈블리어만 생성하는 것이다.
'모종의' 이유로, 컴파일러가 이러한 최적화를 하지 않도록 하려면, 변수를 선언할 때 volatile 을 붙여주면 된다!
// 이제 대입할 때마다 그에 맞는 어셈블리어가 생성된다.
volatile int a = 0;
아, 물론 atomic 을 사용해도 된다. atomic 은 volatile 을 포함하는 녀석이다 😉
이제 SpinLock 을 아래와 같이 제대로 구현할 것이다... LockGuard 도 사용해서 😂
참고로, 우리가 만드는 클래스에 lock 과 unlock 함수가 있으면 std::lock_guard 를 만들때 넣어서 사용할 수 있다!
class SpinLock
{
private:
atomic<bool> _locked = false;
public:
void lock()
{
bool expected = false;
bool desired = true;
while (_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false;
}
}
void unlock()
{
_locked.store(false);
}
};
SpinLock spinLock;
void Add()
{
for (int i = 0; i < 10'000; ++i)
{
lock_guard<SpinLock> lockGuard(spinLock);
++sum;
}
}
위 코드를 보면, compare_exchange_strong 이라는 녀석이 나오는데, 아래 부분의 의미는 다음과 같다 :
if (_locked == expected)
{
expected = _locked;
_locked = desired;
return true;
}
else
{
expected = _locked;
return false;
}
핵심은 _locked 가 expected 와 같은지를 판단하는 것이다. 그런데 expected 는 항상 _locked 의 값을 가지게 되는데...
설명을 잘 보면 expected 변수를 참조하여 사용하고 있다. 따라서, Lock 을 실제로 잡는데 성공했을 때에는, expected 값을 다시 false 로 바꾸어 주어야 한다! 😊
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 1-9. 이벤트 구현 (+ Handle) (0) | 2023.07.14 |
---|---|
[C++ 게임 서버] 1-8. Sleep 개념과 구현 (0) | 2023.07.12 |
[C++ 게임 서버] 1-6. Lock 구현 이론 (0) | 2023.07.11 |
[C++ 게임 서버] 1-5. DeadLock 의 개념과 기초 (+ std::lock) (0) | 2023.07.10 |
[C++ 게임 서버] 1-4. Lock 기초 (lock_guard, unique_lock) (0) | 2023.07.10 |