Spin Lock

lock이 해제될때까지 바쁜 대기(busy waiting)
스핀락은 유저 레벨에서 무한 while 루프를 돌고 있기 때문에 쓰레드 교체를 하지 않고 계속 CPU 자원(코어를 계속 점유)을 소모하면서 버티는 것 -> 컨텍스트 스위칭이 발생하지 않음.

Spin Lock 구현

ver 1.

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 키워드 : 최적화를 하지 말아달라.

  • 왜 0이 안나오는가 ??
    1. 락이 걸려있는가를 확인한다.
    2. 걸려있지 않으면 락을 걸고 통제권을 획득한다.
    • 위 경우에서 만약 두 쓰레드가 i. 번을 동시에 확인하여 락이 걸려있지 않음을 확인한다면?
    • 통제권을 함께 얻는 경우가 발생함. -> i, ii 가 한 묶음으로 동작하지 않음!
    • 즉, 원자성이 보장되지 않음.
  • 해결방안 -> 락을 확인한다 + 락을 건다 가 한 묶음으로 동작해야 함.

ver 2.

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 란?