서버/멀티스레드

15. ThredManager

광란의슈가슈가룬 2024. 8. 1. 02:18

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>;

CorePch.h

#pragma once

#include "Types.h"
#include "CoreMacro.h"
#include "CoreTLS.h"
#include "CoreGlobal.h"

#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

#include <iostream>
using namespace std;

pch.h

#pragma once

#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용을 Windows 헤더에서 제외합니다.

#include "CorePch.h"

 

위와 같이 자주 사용하는 것들을 미리 헤더파일로 만든 후 pch에 추가하면 유용하게 사용할 수 있다.

 

pch란 precompiled header의 약자로 직역하면 미리 컴파일된 헤더이다.

pch는 변경되지 않을 cpp나 헤더들을 한번 빌드한 변경사항이 없으면 따로 빌드하지 않는 파일이다.

따라서 무겁거나 긴 소스코드나 헤더가 있을 때 효과적으로 컴파일 시간을 줄여줄 수 있다.

 

CoreMacro.h

#pragma once

//------------------
//	Crash
//------------------

#define CRASH(cause)				\
{						\
	uint32* crash = nullptr;		\
	/*crash != nullptr 라고 가정해라*/	\
	__analysis_assume(crash != nullptr);	\
	*crash = 0xBADEDCFE;			\
}

//조건에 따라 크래시
#define ASSERT_CRASH(expr)			\
{						\
	if(!expr)				\
	{					\
		CRASH("ASSERT_CRASH");		\
		__analysis_assume(expr);	\
	}					\
}

자주 사용될 메크로 함수들을 저장한 헤더파일이다. 앞으로 서버를 구성하는데 필요한 메크로들을 추가할 예정이다.

 

이번 글에서는 메인 서버에서 스레드를 관리하는 스레드매니저를 큰 틀만 만들 것이다.

 

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()
{
}

스레드매니저는 Launch에서 함수를 받아 그 함수를 실행하는 스레드를 만들어주는 일을 한다. 나머지 코드들은 전에 배운 내용을 활용한 것들이니 따로 설명은 하지 않는다.

 

CoreGlobal.h

#pragma once

extern class ThreadManager* GThreadManager;

class CoreGlobal
{
public:
	CoreGlobal();
	~CoreGlobal();
};

CoreGlobal.cpp

#include "pch.h"
#include "CoreGlobal.h"
#include "ThreadManager.h"

ThreadManager* GThreadManager = nullptr;

// 매니저가 추가될 경우 객체의 생성, 소멸 순서 맞추기
CoreGlobal::CoreGlobal()
{
	GThreadManager = new ThreadManager();
}

CoreGlobal::~CoreGlobal()
{
	delete GThreadManager;
}

CoreGlobal는 만약 매니저나 다른 객체들이 늘어나게 될 경우 객체의 생성과 소멸 순서를 맞추는 역할을 한다.

 

 

Main.cpp

#include "pch.h"
#include "CorePch.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 (int i = 0; i < 5; i++)
	{
		GThreadManager->Launch(ThreadMain);
	}

	GThreadManager->Join();
}

간단하게 5개의 스레드를 만들어 잘 작동하는지 확인해보자

출력 화면

정상적으로 잘 작동하는 모습이다. 지금까지 공부했던 내용을 스레드 매니저로 만들어 관리하게 하였다.

 

메인스레드의 ID가 1번이므로 2~6번까지 출력되는 것을 볼 수 있다.

'서버 > 멀티스레드' 카테고리의 다른 글

17. DeadLock 탐지  (0) 2024.08.04
16. Reader-Writer Lock  (0) 2024.08.02
14. Lock-Free Stack #2  (0) 2024.07.30
13. Lock-Free Stack #1  (0) 2024.07.29
12. Lock-Based Queue/Stack  (0) 2024.07.26