목록Game Dev (261)
KoreanFoodie's Study
[C++ 게임 서버] 2-2. 스마트 포인터 핵심 : 1. 스마트 포인터에는 3종류가 있다. unique_ptr, shared_ptr, weak_ptr. 이 중, unique_ptr 는 소유권 개념을 위한 간단하고 가벼운 스마트 포인터이다. 2. shared_ptr 를 생성하면 사용하고자 하는 타입에 대한 메모리와 참조 카운트 등을 관리하는 제어 블록(Control Block)에 대한 메모리가 둘 다 할당된다. shared_ptr 의 경우 순환 문제가 발생할 수 있으므로, 주의해야 한다! 3. shared_ptr 에 대한 참조 횟수가 사라지면 우리가 사용하고자 하는 데이터에 대한 메모리는 해제되나, 만약 해당 포인터를 참조하는 weak_ptr 가 있을 경우, 제어 블록은 해당 포인터를 참조하는 weak_..
[C++ 게임 서버] 1-25. Reference Counting 핵심 : 1. 멀티 쓰레드 환경에서, 생 포인터를 사용하는 것은 언제나 위험하다. 의도치 않게 delete 된 포인터를 사용하려고 할 수도 있기 때문이다. 2. 일반적으로는 Smart Pointer 를 통해 이런 문제를 해결하는데, 핵심적인 부분은 결국 Reference Count 를 체크하여 nullptr 에 접근하는 것을 막는 것이다! 물론 SharedPtr 까지 제대로 써 주어야 문제를 막을 수 있다. 멀티쓰레드 프로그래밍을 할 때, 생 포인터를 사용하게 되면 의도치 않게 nullptr 에 접근하는 경우를 맞딱뜨리게 된다. 아래의 예시를 보자. 레이스와 터렛이 있다고 하고, 터렛이 레이스를 타겟팅하여 격추 시키는 것을 상상해 보자! ..
[C++ 게임 서버] 1-24. 연습문제 (소수의 갯수 구하기) 핵심 : 1. 연습문제를 풀어보자... (주어진 숫자까지의 소수의 갯수 구하기) 2. 쓰레드 혹은, future 를 통해 구현할 수 있다. 3. thread::hardware_concurrency() 를 이용해, 실제 코어 갯수만큼 쓰레드를 생성하는 것이 더 효율적일 수 있다(그보다 훨씬 많이 생성하는 것보다). 이제 연습문제를 풀어보자. 주어진 숫자에 대해, 1부터 해당 숫자까지 소수의 갯수를 구하는 문제이다. 일단 멀티 쓰레드를 이용할 것이니, 각 쓰레드에 태워 보낼 함수를 다음과 같이 정의하자 : bool IsPrime(int InInput) { if (InInput
[C++ 게임 서버] 1-23. DeadLock 탐지 핵심 : 1. 데드락을 잡는다는 것은, 결국 락을 잡는 방식에 있어 사이클이 형성되는지를 판단하는 것과 동일하다. 2. DFS 를 응용하여, 사이클이 생기는지 여부를 검사하면 DeadLock 상황을 미리 검출할 수 있다. DeadLock 의 경우, 개발 단계에서는 나오지 않으나, 막상 라이브 서비스를 하면 검출되는 경우가 많다. 구조적으로는 데드락이 걸릴 로직이 있음에도, 접속자 수가 많지 않아 데드락이 걸리지 않을 수 있다. 이런 타이밍 적인 이슈에 의존하지 않고, DFS 방식으로 사이클이 있는지를 검사하여, 데드락 문제를 미리 방지할 수 있다. 데드락을 잡는다는 것은, 결국 락을 잡는 방식에 있어 사이클이 형성되는지를 판단하는 것과 동일하기 때문이..
[C++ 게임 서버] 1-22. Reader-Writer Lock 핵심 : 1. 읽는 경우가 쓰는 경우보다 훨씬 많을 경우, Read Lock 과 Write Lock 을 나누어서 사용할 수도 있다. 2. Write Lock 의 경우 쓰레드의 ID 를 기준으로 소유권을 경합하고, Read Lock 의 경우 공유 카운트를 올리는 방식으로 작동한다. 3. Write Lock 을 잡은 상태에서 Read Lock 을 잡는 것은 가능하지만, Read Lock 을 잡은 상태에서 바로 Write Lock 을 잡는 것은 불가능하도록 설계한다. 만약 어떤 컨텐츠에서, 내용을 읽는 경우는 1초에 100번 일어나는데, 실제로 내용을 쓰는 경우는 일주일에 한 번 일어난다고 가정해 보자. 이때, 사실 내용을 읽을 때마다 쓰레드별..
[C++ 게임 서버] 1-21. ThreadManager 핵심 : 1. Thread 를 직접 생성하기보다, ThreadManager 를 통해 생성해 보자. 2. ThreadManager 를 통해 쓰레드를 관리하면, 초기화 및 종료 시점의 작업을 자동화시킬 수 있을 것이다. 3. CRASH 관련 매크로를 유용하게 활용하자. 이제 본격적으로 서버 구축을 시작하기 전에, Thread 관리를 도와줄 ThreadManager 를 만들어 보도록 하겠다. 원리나 기능은 간단하다. 현재로서는 Thread Local Storage 에 각 쓰레드별로 ID 를 부여하고, 생성시에 이를 출력하는 정도의 기능만 만들 것이다. 코드를 보면 이해가 빠르다. ThreadManager.h #pragma once #include #in..
보호되어 있는 글입니다.
[C++ 게임 서버] 1-17, 18, 19. Lock-Free Stack 핵심 : 1. Lock-Free Stack 은 락을 사용하지 않고 여러 쓰레드가 Stack 에 어떻게 하면 제대로 Push/Pop 을 할 수 있는지를 보여준다. popCount 나 참조권/소유권 개념을 활용해 이를 구현할 수 있다. 2. Lock-Free Stack 은 실제로 활용하기에는 까다로우며, Lock 기반의 자료구조보다 더 빠르고 효율적이라고 장담할 수 없다. 결국 경합이 일어났을 때, 실질적으로 삭제를 하는 쓰레드는 1개이어야 하기 때문이다. 3. compare_exchange_weak 은 compare_exchange_strong 과 비교했을 때, spurious fail 이 일어날 수도 있다는 차이가 있다. 그 sp..
[C++ 게임 서버] 1-16. Lock-Based Stack/Queue 핵심 : 1. Lock-Based Queue/Stack 자료구조를 활용하면, 여러 쓰레드가 해당 자료구조를 활용할 때 불필요한 Lock 을 추가로 잡을 필요가 없이, 싱글쓰레드 환경에서처럼 Push/Pop 을 사용할 수 있다. 2. Push/Pop 함수 내부적으로 lock_guard 를 잡아주면 된다. 3. WaitPop 의 경우, Condition Variable 을 이용하여 구현한다. Queue 나 Stack 같은 자료구조에 여러 쓰레드가 동시에 접근해야 한다고 가정해 보자. 지금까지 배운 것을 응용하면, 각 쓰레드가 특정 자료구조에 접근할 때마다 락을 걸고 풀기를 반복해야 하겠지만... 이는 사실 매우 귀찮을 수 있다. 그냥 ..
[C++ 게임 서버] 1-15. Thread Local Storage 핵심 : 1. Thread Local Storage(TLS) 는, 쓰레드 별로 할당된 독립적인 메모리 공간을 의미한다. 2. TLS 를 이용하면, 각 쓰레드별로 변수를 사용해야 할 때 힙이나 데이터 영역에서 별도의 변수 선언 없이 데이터를 참조할 수 있어 불필요한 Race Condition 을 피할 수 있다. 3. TLS 는 컴파일러 의존적으로 선언할 수도 있지만, C++ 에서는 이제 언어 표준에서 해당 기능을 제공한다. Thread Local Storage 란, 쓰레별로 독립된 메모리 영역을 의미한다. 스택은 아래 그림과 같이 별도의 stack 영역이 존재하고, Heap 과 데이터 영역을 공유하는데... TLS 의 경우, 각 쓰레드별..