목록Categories (1104)
KoreanFoodie's Study

[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 의 경우, 각 쓰레드별..

[C++ 게임 서버] 1-14. 메모리 모델 (atomic, 코드 재배치) 핵심 : 1. atomic 연산이란, 말 그대로 해당 연산이 '원자적'으로 수행된다는 뜻이다. '원자적' 이라는 것은, 해당 연산 사이에 다른 연산이 끼어들지 않는다는 것을 의미한다. 2. 컴파일러는 여러 이유(주로 최적화)로 코드를 재배치하는데, 이러한 memory order 는 모든 쓰레드들이 '수정 순서' 에 동의한다는 원칙을 지켜야 한다. 3. 대표적으로 다음 세 가지의 메모리 모델 옵션이 있다 : memory_order_relaxed, memory_order_release, memory_order_seq_cst 원자적 연산이란 무엇일까? 멀티쓰레드 환경에서 원자적 연산을 마치 일시적으로 락을 건다고 생각하기 쉽지만, 정확..

[C++ 게임 서버] 1-13. CPU 파이프라인 (feat. 코드 재배치) 핵심 : 1. 컴파일러는 어셈블리어를 배치하는 과정에서 코드 재배치를 할 수 있다. 이 과정에서 의도치 않은 결과가 도출될 수 있다. 2. CPU 파이프라인은 CPU 가 명령어를 효율적으로 하기 위한 시스템이다. CPU 파이프라인에 대해 공부를 하게 되면, 최적화를 위해 컴파일러가 임의로 어셈블리 코드를 재배치한다는 것을 알 수 있다. 싱글 쓰레드에서는 순차적으로 로직이 실행됨이 보장되지만, 멀티 쓰레드에서는 꼭 의도대로 프로그램이 동작하지 않을 수 있는데... 다음 코드를 보자. volatile bool ready = false; // 가시성, 코드 재배치 int32 x = 0; int32 y = 0; int32 r1 = 0;..

[C++ 게임 서버] 1-12. 캐시 (Temporal Locality, Spatial Locality) 핵심 : 1. RAM 으로부터 데이터를 가져올 때, 자주 쓰일 것으로 예상되는 데이터는 캐시에 저장된다 2. 캐싱 철학에는 Temporal Locality 와 Spatial Locality 가 있는데, 각각 '최근에 사용된 녀석은 다시 사용될 확률이 높다' , '메모리상으로 가까이 있는 녀석은 함께 사용될 확률이 높다' 라는 추론을 기반으로 한다. 3. 실제 메모리 조회를 할 때는 위의 Temporal Locality 와 Spatial Locality 를 고려하여 설계를 하는 것이 좋다. 흔히 우리는 캐시를 통해 값을 조회할 때, 우리가 필요한 정보만 얻어올 것이라고 기대하지만, 실제로는 그렇지 않다..