| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 최소 공통 조상
- 자바스크립트
- 2-SAT
- Binary Lifting
- 분리 집합
- Lock-free Stack
- Github
- 트라이
- Prisma
- Behavior Design Pattern
- R 그래프
- 비트필드를 이용한 dp
- map
- trie
- 그래프 탐색
- PROJECT
- 이분 탐색
- ccw 알고리즘
- localstorage
- SCC
- 게임 서버 아키텍처
- 비트마스킹
- JavaScript
- 벨만-포드
- Strongly Connected Component
- LCA
- Spin Lock
- 강한 연결 요소
- DP
- Express.js
Archives
- Today
- Total
dh_0e
[C++/Game Server] Lock-Based Stack/Queue (+delete, IN/OUT) 본문
Mutex를 활용한 Stack과 Queue
<ConcurrentStack.h>
#include<mutex>
template<typename T>
class LockStack
{
public:
LockStack() {}
// 1. 복사 생성자 삭제: "나랑 똑같은 놈 복사해서 새로 만들지 마!"
// 복사 생성자 금지 : LockStack s2 = s1; 처럼 새로운 객체를 복사해서 만들려고 하면 컴파일 에러 발생.
LockStack(const LockStack&) = delete;
// 2. 복사 대입 연산자 삭제: "이미 있는 놈한테 내 내용물 복사해주기 싫어!"
// 복사 대입 연산자 금지: s2 = s1; 처럼 이미 만들어진 객체에 값을 덮어쓰려 해도 컴파일 에러 발생.
LockStack& operator=(const LockStack&) = delete;
void Push(T value)
{
lock_guard<mutex> lock(_mutex);
_stack.push(std::move(value)); // std::move로 주솟값 빼앗아오기(얕은 복사)로 시간 절약 >> value에는 빈 껍데기만 남음
_condVar.notify_one();
}
bool TryPop(T& value)
{
lock_guard<mutex> lock(_mutex);
if (_stack.empty())return false;
value = std::move(_stack.top());
_stack.pop(); // C++ stack에서 pop과 top이 분리된 이유: pop하는 도중 복사 과정에서의 exception(충돌)을 방지하기 위해서
return true;
}
// empty를 체크하고 나서 Pop을 해도 사실 그 사이에 값이 바뀌었을지 모르기 때문에 의미가 딱히 없음
bool Empty()
{
lock_guard<mutex> lock(_mutex);
return _stack.empty();
}
// Pop할 때 데이터가 없다면 끝나는게 아니라 데이터가 들어올 때까지 기다리는 버전
void WaitPop(T& value)
{
unique_lock<mutex> lock(_mutex); // 조건 변수 사용 시엔 lock_guard가 아닌 unique_lock 사용
_condVar.wait(lock, [this] {return !_stack.empty(); });
value = std::move(_stack.top());
_stack.pop();
}
private:
stack<T> _stack;
mutex _mutex;
condition_variable _condVar;
};
= delete
- C++11 이전에 특정 함수의 기능을 막고 싶을 때, 해당 함수를 private 영역에 선언만 하고 구현은 하지 않는 꼼수를 썼었음
- 이를 public 영역에 delete와 함께 당당하게 써서 설계 의도부터 금지된 것임을 컴파일러에게 알려주기 위해 C++11부터 사용됨
- 주로 사용되는 상황
- 복사가 불가능한 자원을 관리할 때
- Mutex나 File Handle같은 하나만 존재해야 하는 물리적/논리적 자원을 복사한다는 것은 논리적으로 말이 안되기 때문
- 싱글톤(Singleton) 패턴을 만들 때
- 마찬가지로 프로그램 전체에 단 하나의 인객체만 존재해야 하는 클래스를 만들 때 실수로라도 복제되는 것을 막기 위해 사용
- 원치 않는 형변환을 막을 때
- 형변환을 막기 위해 생성자가 아닌 일반 함수에 사용
- ex) 정수만 받아야 하는 함수에 실수가 들어오는 것을 막고 싶을 때 다음과 같이 작성하여 막을 수 있음
void OnlyInt(int a) { /* ... */ }
void OnlyInt(double) = delete; // 실수(double)가 들어오면 컴파일 에러!
- 복사가 불가능한 자원을 관리할 때
<ConcurrentQueue.h>
#include <mutex>
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();
// C++ stack에서 pop과 top이 분리된 이유
// >> pop하는 도중 복사 과정에서의 exception(충돌)을 방지하기 위해서
return true;
}
void WaitPop(T& value)
{
unique_lock<mutex> lock(_mutex);
_condVar.wait(lock, [this] {return !_queue.empty(); });
value = std::move(_queue.front());
_queue.pop();
}
private:
queue<T> _queue;
mutex _mutex;
condition_variable _condVar;
};
<Main.cpp>
#include "ConcurrentQueue.h"
#include "ConcurrentStack.h"
LockStack<int32> st;
LockQueue<int32> que;
void Push()
{
while (1)
{
int32 value = rand() % 100;
que.Push(value);
this_thread::sleep_for(10ms);
}
}
void Pop()
{
while (1)
{
int32 data = 0;
que.WaitPop(OUT data); // OUT: 출력용 데이터라는 뜻 #define OUT 으로 빈껍데기로 저장되어 있음
cout << data << endl;
}
}
int main()
{
thread t1(Push);
thread t2(Pop);
thread t3(Pop);
t1.join();
t2.join();
t3.join();
return 0;
}
IN/OUT/INOUT 메크로
- 아무런 기능이 없는 '빈 껍데기'로 해당 매개변수가 수정되는지, 읽기만 하는지 등 가독성을 위해 이를 표현하는 약속(관례)
#define IN /* 입력용 (보통 const 참조) */
#define OUT /* 출력용 (값 수정됨) */
#define INOUT /* 입력받아서 수정까지 함 */
// 호출할 때
que.WaitPop(OUT data);
// data 이 변수 안에서 값 바뀐다!
- C# 같은 언어엔 out이라는 키워드가 실제로 문법으로 존재
- 보통 Windows 헤더 파일에 존재
'C++ > Game Server' 카테고리의 다른 글
| [C++/Game Server] Thread Manager (+각 서버 file 설명) (0) | 2026.02.11 |
|---|---|
| [C++/Game Server] Lock-Free Stack (with Smart Pointer, Reference Counting) (0) | 2026.02.10 |
| [C++/Game Server] Thread Local Storage (0) | 2026.02.05 |
| [C++/Game Server] 동기화 방식과 메모리 정책 (memory_order) (1) | 2026.02.04 |
| [C++/Game Server] future, promise, packaged_task (1) | 2025.08.08 |