KoreanFoodie's Study
[C++ 게임 서버] 1-21. ThreadManager 본문
[C++ 게임 서버] 1-21. ThreadManager
핵심 :
1. Thread 를 직접 생성하기보다, ThreadManager 를 통해 생성해 보자.
2. ThreadManager 를 통해 쓰레드를 관리하면, 초기화 및 종료 시점의 작업을 자동화시킬 수 있을 것이다.
3. CRASH 관련 매크로를 유용하게 활용하자.
이제 본격적으로 서버 구축을 시작하기 전에, Thread 관리를 도와줄 ThreadManager 를 만들어 보도록 하겠다.
원리나 기능은 간단하다. 현재로서는 Thread Local Storage 에 각 쓰레드별로 ID 를 부여하고, 생성시에 이를 출력하는 정도의 기능만 만들 것이다.
코드를 보면 이해가 빠르다.
ThreadManager.h
#pragma once
#include <thread>
#include <functional>
/*------------------
ThreadManager
-------------------*/
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;
};
ThreadManager.cpp
#include "pch.h"
#include "ThreadManager.h"
#include "CoreTLS.h"
#include "CoreGlobal.h"
/*------------------
ThreadManager
-------------------*/
ThreadManager::ThreadManager()
{
// Main Thread
InitTLS();
}
ThreadManager::~ThreadManager()
{
Join();
}
void ThreadManager::Launch(function<void(void)> callback)
{
LockGuard guard(_lock);
_threads.push_back(thread([=]()
{
InitTLS();
callback();
DestroyTLS();
}));
}
void ThreadManager::Join()
{
for (thread& t : _threads)
{
if (t.joinable())
t.join();
}
_threads.clear();
}
void ThreadManager::InitTLS()
{
static Atomic<uint32> SThreadId = 1;
LThreadId = SThreadId.fetch_add(1);
}
void ThreadManager::DestroyTLS()
{
}
위에서 CoreTLS 에는 그냥 thread_local 한 변수를 하나 선언만 해 줄 것이다.
Launch 함수를 활용하면, callback 으로 들어가는 함수를 수행하는 쓰레드가 하나 생성되어 쓰레드들을 담는 vector 에 넣어준다.
InitTLS 와 DestroyTLS 는 초기화 및 종료시 정리를 하는 작업을 수행할 것이다.
이제 아래와 같이 main 함수에서 ThreadManager 를 활용하면...
CoreGlobal Core;
void ThreadMain()
{
while (true)
{
cout << "Hello ! I am thread... " << LThreadId << endl;
this_thread::sleep_for(1s);
}
}
int main()
{
for (int32 i = 0; i < 5; i++)
{
GThreadManager->Launch(ThreadMain);
}
GThreadManager->Join();
}
아래와 같이, 1초마다 각 쓰레드별로 Id 값을 출력하는 간단한 동작을 확인할 수 있다 😊
아, 참고로 앞으로 Crash 관련 이슈를 확인할 매크로를 하나 더 기록한다.
CoreMacro.h
/*---------------
Crash
---------------*/
#define CRASH(cause) \
{ \
uint32* crash = nullptr; \
__analysis_assume(crash != nullptr); \
*crash = 0xDEADBEEF; \
}
#define ASSERT_CRASH(expr) \
{ \
if (!(expr)) \
{ \
CRASH("ASSERT_CRASH"); \
__analysis_assume(expr); \
} \
}
위에서 CRASH 를 사용하면 일부러 크래시를 낼 수 있고, ASSERT_CRASH 를 사용하면 expr 값에 따라 crash 를 발생시킬 것이다.
참고로, __analysis_assume 의 경우, 인자가 항상 참이라고 컴파일러에게 알려주어 경고를 줄일 수 있다고 한다. 즉, 위에서 __analysis_assume 을 사용한 이유는... 우리가 일부러 크래시를 내는 것을 원하기 때문에, 혹시라도 모를 컴파일러의 오지랖(?)을 막기 위해서 쓴 것이다 🤣
자세한 내용은 이 링크를 참고하자 😉
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 1-23. DeadLock 탐지 (0) | 2023.08.08 |
---|---|
[C++ 게임 서버] 1-22. Reader-Writer Lock (0) | 2023.08.07 |
[C++ 게임 서버] 1-20. Lock-Free Queue (0) | 2023.07.26 |
[C++ 게임 서버] 1-17, 18, 19. Lock-Free Stack (0) | 2023.07.26 |
[C++ 게임 서버] 1-16. Lock-Based Stack/Queue (0) | 2023.07.25 |