스핀락이란?
- 멀티쓰레드 환경에서 동기화를 위해 사용되는 기법 중 하나
- 임계 영역에 동시에 접근하는 것을 막기 위해 사용된다.
스핀 락의 동작 방식
- 하나의 쓰레드가 임계 영역에 들어가려고 할 때 다른 쓰레드들은 스핀락을 획득하기 위해 계속 확인하고 대기한다
- 스핀락을 획득한 쓰레드가 임계 영역을 빠르게 사용하고 반환한다면 대기하던 쓰레드들은 스핀락을 획득하고 임계 영역에 접근할 수 있다.
- 대기하고 확인하는 것 또한 코스트이기 때문에 임계 영역에 대한 접근이 짧고 빠른 경우에 적합하다.
스핀 락의 구현
#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 |
댓글