lock이 해제될때까지 바쁜 대기(busy waiting)
스핀락은 유저 레벨에서 무한 while 루프를 돌고 있기 때문에 쓰레드 교체를 하지 않고 계속 CPU 자원(코어를 계속 점유)을 소모하면서 버티는 것 -> 컨텍스트 스위칭이 발생하지 않음.
class SpinLock
{
private:
volatile bool _locked = false;
public:
void lock()
{
while(_locked)
{
//바쁜 대기
}
_locked = true;
}
void unlock()
{
_locked = false;
}
}
int sum = 0;
SpinLock spinLock;
void Add()
{
for(int i = 0 ; i < 10'0000 ; i++)
{
lock_guard<SpinLock> guard(spinLock);
sum++;
}
}
void Sub()
{
for(int i = 0 ; i < 10'0000 ; i++)
{
lock_guard<SpinLock> guard(spinLock);
sum--;
}
}
int main()
{
std::thread t1(Add);
std::thread t2(Sub);
t1.join();
t2.join();
cout << sum << endl;
return 0;
}
실행 결과
-36323
⭐️ TIP C++ 에서의
volatile키워드 : 최적화를 하지 말아달라.
락을 확인한다 + 락을 건다 가 한 묶음으로 동작해야 함.class SpinLock
{
private:
atomic<bool> _locked = false;
public:
void lock()
{
// CAS(Compare-And-Swap)
bool expected = false;
bool desired = true;
/*
* CAS 의사 코드(커널 레벨에서 동작하는 것이므로 깊게 생각할 필요 없음)
* if(_locked == expected) 락 획득 성공
* {
* expected = _locked;
* _locked = desired;
* return true;
* }
* else 락 획득 실패
* {
* expected = _locked;
* return false;
* }
*/
while (_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false; //expected는 참조를 갖기 때문에 값이 계속 변함 -> 계속 초기화해줘야 함
}
/*
while(_locked)
{
//바쁜 대기
}
_locked = true;
*/
}
void unlock()
{
_locked.store(false); //atomic하게 대입
}
}
⭐️ TIP
atomic<typename T>에는 volatile이 포함되어 있다!
bool compare_exchange_strong(bool& _Expected, const bool _Desired)
실행 결과
0
참조 링크
CAS 란?