본문 바로가기
게임 서버/[Inflearn_rookiss]올인원_클라&서버 연동

5. 스핀 락

by 헛둘이 2023. 12. 11.

스핀락이란?

- 멀티쓰레드 환경에서 동기화를 위해 사용되는 기법 중 하나
- 임계 영역에 동시에 접근하는 것을 막기 위해 사용된다.
 

스핀 락의 동작 방식

- 하나의 쓰레드가 임계 영역에 들어가려고 할 때 다른 쓰레드들은 스핀락을 획득하기 위해 계속 확인하고 대기한다
- 스핀락을 획득한 쓰레드가 임계 영역을 빠르게 사용하고 반환한다면 대기하던 쓰레드들은 스핀락을 획득하고 임계 영역에 접근할 수 있다.
- 대기하고 확인하는 것 또한 코스트이기 때문에 임계 영역에 대한 접근이 짧고 빠른 경우에 적합하다.
 

스핀 락의 구현

#include <iostream>
#include <thread>
#include <vector>
#include <windows.h>
#include <atomic>
#include <mutex>
using namespace std;

class Lock
{
public:
	void lock()
	{
		while (flag) {}
		flag = true;
	}
	void unlock()
	{
		flag = false;
	}


private:
	atomic<bool> flag = false;
};

vector<int> v;
Lock m;

void Push()
{
	for (int i = 0; i < 10000; ++i)
	{
		lock_guard<Lock> lockGuard(m);
		v.push_back(i);
	}
}

int main()
{
	v.reserve(1000000);

	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	cout << v.size() << endl;

	return 0;
}

- 대략 이렇게 구현하면 되지 않을까 생각이 들지만 그렇지 않다
- while (flag) {} 이 부분에 다수의 스레드가 동시에 진입할 수 있기 때문이다.
- 그렇다면 Lock을 하기위해 또 Lock이 필요하다? 말이 안된다
- 이 부분을 해결하기 위한 개념으로 CAS라는 개념이 있다.
 

CAS (Compare and Swap)

- 비교하고 바꾸는 과정을 하나의 명령어로 처리하는 것이다.
- atomic에서 이 기능을 지원하는데, compare_exchange_strong이라는 함수를 통해 CAS를 구현할 수 있다.

atomic.compare_exchange_strong(expected (예상한 값), desired (예상했던 상황에 들어가야 하는 값))

bool expected = false; // 예상되는 값
bool desired = true; // 예상되는 상황에 들어가야 하는 값

if (_flag == expected) //현재 플래그가 예상되는 값인지 확인
{
	_flag = desired; // 예상되는 상황에 들어가야 하는 값으로 덮어쓴다
 	return true; // true를 리턴
}
else
{
	expected = _flag; // expected에 flag 값이 들어간다
	return false; // false를 리턴
}

- 위의 if/else 문이 compare_exchange_strong 한 줄로 처리된다고 보면 된다.
- 아래는 수정된 코드
 

#include <iostream>
#include <thread>
#include <vector>
#include <windows.h>
#include <atomic>
#include <mutex>
using namespace std;

class Lock
{
public:
	void lock()
	{
		bool expected = false;
		bool desired = true;
		while (flag.compare_exchange_strong(expected, desired) == false)
			expected = false; // false일 경우 expected = flag가 실행되므로 다시 false로 돌려놓아야 한다.


	}
	void unlock()
	{
		flag = false;
	}


private:
	atomic<bool> flag = false;
};

vector<int> v;
Lock m;

void Push()
{
	for (int i = 0; i < 10000; ++i)
	{
		lock_guard<Lock> lockGuard(m);
		v.push_back(i);
	}
}

int main()
{
	v.reserve(1000000);

	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	cout << v.size() << endl;

	return 0;
}

 

'게임 서버 > [Inflearn_rookiss]올인원_클라&서버 연동' 카테고리의 다른 글

7. 이벤트와 조건변수  (0) 2023.12.11
6. 데드 락  (0) 2023.12.11
4. Lock 기초  (0) 2023.12.11
3. CPU 파이프라인 및 공유 자원  (0) 2023.12.11
2. 멀티 쓰레드 실습  (0) 2023.12.11

댓글