KoreanFoodie's Study
[C++ 게임 서버] 1-16. Lock-Based Stack/Queue 본문
[C++ 게임 서버] 1-16. Lock-Based Stack/Queue
핵심 :
1. Lock-Based Queue/Stack 자료구조를 활용하면, 여러 쓰레드가 해당 자료구조를 활용할 때 불필요한 Lock 을 추가로 잡을 필요가 없이, 싱글쓰레드 환경에서처럼 Push/Pop 을 사용할 수 있다.
2. Push/Pop 함수 내부적으로 lock_guard 를 잡아주면 된다.
3. WaitPop 의 경우, Condition Variable 을 이용하여 구현한다.
Queue 나 Stack 같은 자료구조에 여러 쓰레드가 동시에 접근해야 한다고 가정해 보자.
지금까지 배운 것을 응용하면, 각 쓰레드가 특정 자료구조에 접근할 때마다 락을 걸고 풀기를 반복해야 하겠지만... 이는 사실 매우 귀찮을 수 있다.
그냥 싱글 쓰레드 환경에서 Push/Pop 을 간단히 쓰듯 사용하고 싶을 때는 어떻게 해야 할까? 그럴 경우, 자료 구조 자체가 Lock 을 사용하도록 만들면 된다!
즉, Push/Pop 을 할 때 알아서 Lock 을 잘 잡았다가 풀 수 있도록 만들어 주면 되는데... 실제 코드는 다음과 같이 나올 것이다.
template<typename T>
class LockStack
{
public:
LockStack() { }
LockStack(const LockStack&) = delete;
LockStack& operator=(const LockStack&) = delete;
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value));
_condVar.notify_one();
}
bool TryPop(T& value)
{
lock_guard<mutex> lock(_mutex);
if (_stack.empty())
return false;
// empty -> top -> pop
value = std::move(_stack.top());
_stack.pop();
return true;
}
void WaitPop(T& value)
{
unique_lock<mutex> lock(_mutex);
_condVar.wait(lock, [this] { return _stack.empty() == false; });
value = std::move(_stack.top());
_stack.pop();
}
private:
stack<T> _stack;
mutex _mutex;
condition_variable _condVar;
};
특별할 것은 없고, 그냥 예전에 하던 대로 lock_guard 를 잡아주는데, 이제는 함수 내부에서 잡아준다는 차이가 있다.
그리고 WaitPop 의 경우, condition_variable 을 활용하여 TryPop 과는 조금 다른 방식으로 구현하였다!
Lock-Based Queue 의 구현도, 위의 Stack 과 거의 동일하다.
template<typename T>
class LockQueue
{
public:
LockQueue() { }
LockQueue(const LockQueue&) = delete;
LockQueue& operator=(const LockQueue&) = delete;
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_queue.push(std::move(value));
_condVar.notify_one();
}
bool TryPop(T& value)
{
lock_guard<mutex> lock(_mutex);
if (_queue.empty())
return false;
value = std::move(_queue.front());
_queue.pop();
return true;
}
void WaitPop(T& value)
{
unique_lock<mutex> lock(_mutex);
_condVar.wait(lock, [this] { return _queue.empty() == false; });
value = std::move(_queue.front());
_queue.pop();
}
private:
queue<T> _queue;
mutex _mutex;
condition_variable _condVar;
};
위의 자료구조를 활용하면, main 함수에서는 싱글쓰레드 환경처럼 자료구조를 사용할 수 있을 것이다 😉
LockQueue<int32> q;
LockStack<int32> s;
void Push()
{
while (true)
{
int32 value = rand() % 100;
q.Push(value);
this_thread::sleep_for(10ms);
}
}
void Pop()
{
while (true)
{
int32 data = 0;
if (q.TryPop(OUT data))
cout << data << endl;
}
}
int main()
{
thread t1(Push);
thread t2(Pop);
thread t3(Pop);
t1.join();
t2.join();
t3.join();
}
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 1-20. Lock-Free Queue (0) | 2023.07.26 |
---|---|
[C++ 게임 서버] 1-17, 18, 19. Lock-Free Stack (0) | 2023.07.26 |
[C++ 게임 서버] 1-15. Thread Local Storage (0) | 2023.07.24 |
[C++ 게임 서버] 1-14. 메모리 모델 (atomic, 코드 재배치) (0) | 2023.07.21 |
[C++ 게임 서버] 1-13. CPU 파이프라인 (feat. 코드 재배치) (0) | 2023.07.19 |
Comments