목록Game Dev/Game Server (78)
KoreanFoodie's Study
[C++ 게임 서버] 1-5. DeadLock 의 개념과 기초 (+ std::lock) 핵심 : 1. DeadLock 상황은, 여러 쓰레드들이 Lock 을 서로 잡고 있어 풀거나 새로 잡지 못하는 교착 상태를 의미한다. 2. DeadLock 상황은 Lock 을 잡는 순서를 잘 조절하거나, 적절한 Lock 을 사용하여 회피할 수 있다. 3. std::lock 은 여러 개의 Lock 을 DeadLock 없이 한꺼번에 잡을 수 있도록 도와준다. 다음과 같이, Lock 이 2개 존재하고 연산을 위해 Lock 2 개를 전부 잡아야 한다고 가정하자. int sum = 0; mutex mutexAdd; mutex mutexSub; void Add() { for (int i = 0; i < 1000; ++i) { mu..
[C++ 게임 서버] 1-1. 멀티쓰레드 개론 핵심 : 1. Critical Section 에 진입할 때는 mutex 등을 활용하여 Lock 을 걸면 Race Condition 을 해소할 수 있다. 2. 일반적으로 직접 lock/unlock 을 해주기 보다, lock_guard 나 unique_lock 을 사용하여 RAII 방식으로 Lock 을 걸어 주는 것이 좋다. 3. unique_lock 의 경우, defer_lock 등의 옵션을 주어 Lock Guard 를 좀 더 유연하게 사용할 수 있다. 다만 lock_guard 에 비해 메모리를 더 많이 차지하고 무거을 것이다. 다음과 같이 여러 쓰레드를 이용해 벡터에 원소를 삽입한다고 해 보자 : vector vec; void Push() { for (int..
[C++ 게임 서버] 1-3. atomic 에 대하여 핵심 : 1. ++, -- 같은 간단한 연산조차, 사실 어셈블리어에서는 '값 복사' -> '복사된 값에 연산 수행' -> '복사된 값을 다시 대입' 같은 과정으로 쪼개진다 2. 만약 특정 연산이 진행 중일 경우, 다른 연산이 끼어들지 않도록 만들고 싶다면, 해당 변수를 atomic 으로 선언할 수 있다. 3. 일반적으로 atomic 연산은 일반 연산보다 느리므로, 남용은 지양하여야 한다. 어떤 연산이 atomic (원자적) 으로 이루어진다는 것은, 해당 연산이 진행되는 중간에 다른 연산이 동시에 진행되지 않는다는 것을 의미한다. 이전에 여러 쓰레드들이 공유 자원에 접근하려 할 때 Race Condition 이 발생할 수 있다고 이야기한 적이 있는데, ..
쓰레드 생성 핵심 : 1. 쓰레드를 생성하는 방법은 간단하다. 그냥 std::thread 에 알맞은 함수를 넣어주기만 하면 된다. 2. 쓰레드를 닫아줄 때는 join 으로 닫아주면 된다! 닫기 전, joinable 로 join 가능한지를 체크한다. 3. 쓰레드 객체 생성 시, 함수의 인자에 맞게 갯수를 넣어주어야 잘 동작할 것이다. 쓰레드 객체 생성자 내부는 가변 길이 템플릿으로 구현되어 있다. 이제 쓰레드를 생성하는 기초적인 실습부터 진행해 보자 😂 #include "pch.h" #include #include "CorePch.h" #include #include #include #include using namespace std; void HelloThread() { cout
[C++ 게임 서버] 1-1. 멀티쓰레드 개론 핵심 : 1. 멀티쓰레드 프로그래밍은 식당을 운영하는 것과 비슷하다. 테이블을 프로세스, 테이블에서 일하는 직원을 쓰레드, 영혼을 코어라고 생각하면 편하다. 2. 쓰레드는 힙 영역을 공유하지만, 각각의 정보를 스택에 저장한다. 따라서 힙 영역에 접근할 때는 Race Condition 이 발생할 수 있다. 멀티쓰레드 프로그래밍은 식당을 운영하는 것과 비슷하다. 테이블을 프로그램, 직원을 쓰레드, 영혼을 CPU 코어라고 생각하자. 그럼 영혼이 직원에게 들어가야 프로그램이 돌아갈 것이다. 이제 우리가 해야 할 일은, 영혼을 직원에게 어떻게 분배해야 좋을지 구조를 설계하고 코드를 짜는 것이다! 실제로 위의 스샷처럼, CPU 코어를 적절히 배분하면서 생기는 문제를 잘..
Asio 는 Asynchronous IO 의 약자이다. Asio 를 이해하기 위해서는, 다음과 같은 용어들을 알고 있어야 한다. IO Service 클래스 : 커널에서 발생한 입출력 이벤트를 디스패치 해주는 기능을 수행. 디스패치란, 우선순위가 높은 작업이 우선적으로 수행될 수 있도록 시스템 자원을 할당하는 것을 의미한다. IO Service 클래스는 커널에서 발생한 네트워크상의 접속 및 데이터 처리 이벤트를 처리한다. 종단점(Endpoint) : 네트워크 상의 특정 컴퓨터에서 실행되고 있는 하나의 프로그램. 구체적으로 IP 주소와 포트 번호의 한 쌍을 종단점이라고 함. DNS(Domain Name Server) : Boost Asio 에서는 도메인 주소를 이용, IP 주소를 알아내어 종단점을 구한다. ..
IOCP는 CPU 개수만큼만 쓰레드를 쓰는 서버이다. IOCP 를 사용한 모든 Send/Recv 는 Overlapped I/O 로 동작한다. 멀티쓰레드에서 동작하며, 콜백함수들을 동시에 동작해서 성능을 높인다. IOCP가 사용하는 IOCP 객체는 커널 안에서 관리된다. 커널 내에는 DEVICE LIST 라는 자료구조가 존재하는데, 소켓을 IOCP 에 등록하면 IOCP 객체는 DEVICE_LIST 에 소켓들을 등록해서 처리한다. 프로그래머는 DEVICE_LIST 를 직접 보거나 제어할 수 없다. 흐름을 다음과 같이 요약해 볼 수 있을 것 같다 : I/O 디바이스(소켓, FD) IOCP 에 등록 : CreateIoCompletionPrt 함수를 통해 디바이스와 CompletionPort(이하 CP) 를 바인..
먼저 Message Pump 란, 한 메시지를 처리하는 도중 메시지 큐로부터 다른 메시지를 꺼내 이를 처리하는 코드를 의미한다. 윈도우 어플리케이션의 경우, UI 및 사용자 입력 처리하는 과정에서 메시지 펌프를 적극 활용하고 있다. 기초적인 Message Loop 코드는 다음과 같다. MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } 그런데 GetMessage 의 경우 데드타임(메시지큐에 메시지가 없어 GetMessage 가 놀고 있는 시간)을 제대로 활용할 수 없어, PeekMessage 를 많이 활용한다. PeekMessage 는 메시지 큐에 메시지가 있든 없든 리턴을 하기 때문..