KoreanFoodie's Study

[C++ 게임 서버] 1-15. Thread Local Storage 본문

Game Dev/Game Server

[C++ 게임 서버] 1-15. Thread Local Storage

GoldGiver 2023. 7. 24. 17:24

Rookiss 님의 '[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 를 들으며 배운 내용을 정리하고 있습니다. 관심이 있으신 분은 꼭 한 번 들어보시기를 추천합니다!

[C++ 게임 서버] 1-15. Thread Local Storage

핵심 :

1. Thread Local Storage(TLS) 는, 쓰레드 별로 할당된 독립적인 메모리 공간을 의미한다.

2. TLS 를 이용하면, 각 쓰레드별로 변수를 사용해야 할 때 힙이나 데이터 영역에서 별도의 변수 선언 없이 데이터를 참조할 수 있어 불필요한 Race Condition 을 피할 수 있다.

3. TLS 는 컴파일러 의존적으로 선언할 수도 있지만, C++ 에서는 이제 언어 표준에서 해당 기능을 제공한다.

Thread Local Storage 란, 쓰레별로 독립된 메모리 영역을 의미한다. 스택은 아래 그림과 같이 별도의 stack 영역이 존재하고, Heap 과 데이터 영역을 공유하는데...

TLS 의 경우, 각 쓰레드별로 필요한 변수가 있을 때, 경합 없이 데이터를 참조하여 사용할 수 있다는 장점이 있다.

 

그렇다면 쓰레드 별로 독립된 메모리, 변수를 세팅할 수 있는 것이 언제 필요할까?

뭐, 쓰레드 별로 독립된 작업을 하는 경우라면, 굳이 힙 영역에 private 변수를 각각 쓰레드 별로 선언해 주는 것보다는 TLS 를 사용하는 것이 더 합리적일 것이다. 쓰레드별로 별도의 전역변수를 만드는 (즉, 데이터 영역을 사용) 하는 것도 별로 좋은 방법이 아니다.

 

마이크로소프트에서 보여주는 모식도

간단한 예제코드를 보면서, TLS 를 어떻게 사용하는지 예시만 보고 넘어가도록 하자.

// 컴파일러 의존적인 방법. 올드한 방법이다.
__declspec(thread) int32 value;

// 언어 표준에서 지원하는 방법. 아래 방법을 사용하자.
thread_local int32 LThreadId = 0;

void ThreadMain(int i)
{
	LThreadId = i;

	cout << "Thread num : " << LThreadId << endl;
}

int main()
{
	vector<thread> vec;

	for (int i = 0; i < 10; ++i)
	{
		vec.push_back(thread(ThreadMain, i));
	}

	for (int i = 0; i < vec.size(); ++i)
	{
		vec[i].join();
	}
}

위 코드를 실행하면, 아래와 같이 각 쓰레드별로 넘버가 잘 출력되는 것을 확인할 수 있다 :

Comments