ThreadManager가 하는 역할은 server에서 사용하는 쓰레드 자체, TLS 를 관리하기 위함이다.
ThreadManager 생성하기 앞서 여러 곳에 유용하게 쓰일 수 있는 유틸 코드를 정의해보자.
먼저 app을 crash, assert하는 define이다.
CoreMacro.h
/*----------------------
CRASH
----------------------*/
#define CRASH(cause) \
{ \
uint32* crash = nullptr; \
__analysis_assume(crash != nullptr); \
*crash = 0x1DEADEBB; \
}
#define ASSERT_CRASH(expr) \
{ \
if (!(expr)) \
{ \
CRASH("ASSERT_CRASH") \
__analysis_assume(expr); \
} \
}
__analysis_assume은 파라미터가 참이라고 가정하라는 명령이다.
Type 관련 정의도 몇 가지 추가한다.
Types.h
#pragma once
#include <mutex>
#include <atomic>
using BYTE = unsigned char;
using int8 = __int8;
using int16 = __int16;
using int32 = __int32;
using int64 = __int64;
using uint8 = unsigned __int8;
using uint16 = unsigned __int16;
using uint32 = unsigned __int32;
using uint64 = unsigned __int64;
template<typename T>
using Atomic = std::atomic<T>;
using Mutex = std::mutex;
using CondVar = std::condition_variable;
using UniqueLock = std::unique_lock<std::mutex>;
using LockGuard = std::lock_guard<std::mutex>;
굳이 Atomic을 std::atomic<T> 대신 사용하는 이유는 오른쪽 항만 바꾸면 Atomic을 쓰는 모든 코드를 한꺼번에 바꿀 수 있기 때문이다.
ThreadManager는 여러 쓰레드를 관리하고 있고 이들의 시작, 종료를 관리한다.
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;
};
CoreTLS.h, CoreTLS.cpp
#pragma once
extern thread_local uint32 LThreadId;
#include "pch.h"
#include "CoreTLS.h"
thread_local uint32 LThreadId = 0;
ThreadManager.cpp
#include "pch.h"
#include "ThreadManager.h"
#include "CoreTLS.h"
#include "CoreGlobal.h"
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()
{
}
Launch메서드를 보면 callback 실행 전 후에 TLS초기화, 정리 코드가 있다. InitTLS에서는 thread별 ID를 부여한다.
ThreadManager를 생성하는 클래스를 하나 둔다.
CoreGlobal.h, CoreGlobal.cpp
#pragma once
extern class ThreadManager* GThreadManager;
class CoreGlobal
{
public:
CoreGlobal();
~CoreGlobal();
};
#include "pch.h"
#include "CoreGlobal.h"
#include "ThreadManager.h"
ThreadManager* GThreadManager = nullptr;
CoreGlobal::CoreGlobal()
{
GThreadManager = new ThreadManager();
}
CoreGlobal::~CoreGlobal()
{
delete GThreadManager;
}
사용은 아래와 같이 하면 된다.
GameServer.cpp
#include "pch.h"
#include "ThreadManager.h"
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();
}
만약 컨텐츠 부분에서 엔진쪽 객체를 만드는 게 마음에 들지 않는다면 아래와 같이 바꿔쓴다.
CoreGlobal.h, CoreGlobal.cpp
#pragma once
extern class ThreadManager* GThreadManager;
#include "pch.h"
#include "CoreGlobal.h"
#include "ThreadManager.h"
ThreadManager* GThreadManager = nullptr;
class CoreGlobal
{
public:
CoreGlobal()
{
GThreadManager = new ThreadManager();
}
~CoreGlobal()
{
delete GThreadManager;
}
} GCoreGlobal;
GameServer.cpp
#include "pch.h"
#include "ThreadManager.h"
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. AssertCrash 함수 사용 이유와 동작 순서는?
- 파라미터가 참인지 아닌지 확인하고 크래시 내기 위해 사
- __analysis_assume은 컴파일러가 오류로 인식하는 것을 방지하기 위해 사용한 것.
2. InitTLS가 하는 역할은?
- ThreadManager를 통해서 동작하는 thread에 id를 발급하기 위해서 사용.
- fetch_add는 변수를 더하고 결과 값을 반환함.
참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)
Deadlock 탐지 (0) | 2021.09.06 |
---|---|
Read-Writer lock (0) | 2021.09.03 |
Lock free stack #3 (0) | 2021.08.31 |
Lock free stack #2 (0) | 2021.08.26 |
Lock free stack (0) | 2021.08.25 |
댓글 영역