Thread Manager
카테고리: Server
인프런에 있는 루키스님의 게임 서버 강의를 듣고 정리한 내용입니다.
Thread Local Storage (TLS)
Thread Local Storage (TLS)는 멀티스레드 환경에서 각 스레드가 고유한 데이터를 가질 수 있도록 해주는 메커니즘이다. 쓰레드별로 Stack 메모리는 가지고 있지만 스택은 전역 변수, static 변수와 같은 데이터를 다룰 수 없다. 이런 경우를 해결하기 위해 TLS가 필요하다.
TLS를 사용하는 방법은 간단한데, 변수 앞에 thread_local 키워드를 붙여주면 된다. TLS를 사용하는 가장 대표적인 예시는 다음과 같다.
- 1) 중구 난방인 Thread ID를 1부터 깔끔하게 정리하고 싶은 경우
- 2) 쓰레드마다 별도의 로그 관리
thread_local std::ostringstream thread_log;
void log_message(const std::string& message) {
thread_log << message << std::endl;
}
void thread_function(int id) {
log_message("Thread " + std::to_string(id) + " starting");
// Do something
log_message("Thread " + std::to_string(id) + " ending");
std::cout << thread_log.str(); // 각 스레드의 로그를 출력
}
int main() {
std::thread t1(thread_function, 1);
std::thread t2(thread_function, 2);
t1.join();
t2.join();
return 0;
}
Macro
개발을 하다보면 특정 상황에서 크래시를 내고 싶은 상황이 있다. 이런 경우 강제로 크래쉬를 발동시키게 하는데, 애매하게 3/0
같은 연산을 해버리면 크래쉬가 나기 전에 컴파일러 단에서 오류로 잡아버린다.
#define CRASH(cause) \
{ \
uint32* crash = nullptr; \
__analysis_assume(crash != nullptr); \
*crash = 0xDEADBEEF; \
}
#define ASSERT_CRASH(expr) \
{ \
if (!(expr)) \
{ \
CRASH("ASSERT_CRASH"); \
__analysis_assume(expr); \
} \
}
- 오른쪽 끝에 \를 붙여주면 여러 줄의 define 선언 가능
__analysis_assume(crash != nullptr)
: 컴파일러에게 crash는 null이 아니야 라고 거짓말- 해당 조건이 true인지 아닌지를 가지고, 조건부로 crash 발동
Thread Manager
class ThreadManager
{
public:
ThreadManager();
~ThreadManager();
void Launch(function<void(void)> callback);
void Join();
static void InitTLS();
static void DestroyTLS();
private:
Mutex _lock;
vector<thread> _threads;
};
Thread Manager 클래스를 정의해 쓰레드들을 관리하면 여러가지 이점이 있다. 예를 들어 소멸자에서 join()함수들을 호출할 수 있고, 동일한 작업을 하는 쓰레드들도 편하게 생성할 수 있다. 하지만 가장 중요한 역할은 각 스레드의 TLS를 관리하는 것이다.
void ThreadManager::Launch(function<void(void)> callback)
{
LockGuard guard(_lock);
_threads.push_back(thread([=]()
{
InitTLS();
callback();
DestroyTLS();
}));
}
void ThreadManager::InitTLS()
{
static Atomic<uint32> SThreadId = 1;
LThreadId = SThreadId.fetch_add(1);
}
main() 쓰레드에서 쓰레드를 생성하면 보통은 callback 함수만 호출하게 된다. 하지만 위 예시에서는 callback 함수의 호출 전/후로 TLS 데이터를 관리하는 것을 볼 수 있다. 여기에서는 Thread ID를 static 변수로 지정해 하나씩 늘려나가는 데이터를 관리하게 된다.
댓글 남기기