목록Categories (1104)
KoreanFoodie's Study

[C++ 게임 서버] 1-11. future, promise, packaged_task 핵심 : 1. future, promise, packaged_task 는 단발성 이벤트를 비동기적으로 실행할 때 유용하다. 2. 세 녀석의 차이는 Level of Control 에 달려 있다. future 는 특정 함수의 작업을 맡기고, packaged_task 는 작업을 만든 다음, 쓰레드에 이를 실어 보낸다. 마지막으로, promise 는 작업을 실어 보내면서, 동시에 실어 보내는 함수 내부에서 future 에 넘길 값을 커스텀하게 세팅할 수 있다. 3. '비동기적'인 작업과 '멀티쓰레드' 는 다른 의미이다. 어떤 작업이 비동기적으로 실행된다고 해서, 꼭 멀티쓰레드를 이용할 필요는 없다. 사실 자주 쓰이는 녀석들은..

[C++ 게임 서버] 1-10. Condition Variable (조건 변수) 핵심 : 1. 조건 변수(Condition Variable) 은 유저 레벨 오브젝트로, 동일한 프로그램 내에서 접근 가능하다. 2. 조건 변수를 이용하면 하나의 쓰레드 혹은 모든 쓰레드에 시그널을 전달할 수 있다. 3. 조건 변수는 Lock 과 페어를 이루어 사용한다! 저번에 Handle 을 이용해 이벤트를 구현했던 것을 조금 발전시켜, 이번에는 condition variable 을 사용해볼 것이다. 사실 condition variable 은 Handle 을 사용하는 것보다 조금 더 우아한(?) 방식이며, 조건을 체크하여 락을 잡고 푸는 기능을 제공해 준다. 또한 condition variable 은 유저 레벨 오브젝트이므로..

[C++ 게임 서버] 1-9. 이벤트 구현 (+ Handle) 핵심 : 1. 이벤트를 사용하면 SpinLock 에 비해 CPU 낭비를 줄일 수 있는 장점이 있다. 2. 이벤트의 개념은 간단하다. 핸들을 만든 후, Consumer 는 핸들을 참고하여 깨울 때까지 준비 상태에 들어가고, Producer 가 준비가 되었을 때 핸들에게 이벤트를 보내기만 하면 된다. 3. 핸들은 커널 오브젝트에 해당하여, 여러 쓰레드가 이를 활용할 수 있다. 이제 Lock 구현을 함에 있어 '무작정 기다리기' 와 '잠깐 기다리기' 를 구현해 보았으니, 마지막 순서가 남아있다. 바로... '제 3자가 깨워주기' 인데, 여기서 '제 3자'라 함은 일반적으로 OS 의 스케줄러를 의미하고, '깨워준다' 는 것은 해당 쓰레드에 시그널을 ..

[C++ 게임 서버] 1-8. Sleep 개념과 구현 핵심 : 1. Lock 을 잡을 수 없을 때, 일정 시간 동안 기다리게 할 때 sleep 을 사용한다. 2. Sleep 을 사용하면 일정 시간 동안 해당 쓰레드는 준비 상태에 있다가 다시 돌아온다. 이 시간동안에는 CPU 자원을 사용하지 않는다. 대신 Kernel Space 로 전환되는 과정에서의 비용은 발생한다(컨텍스트 스위칭). 3. yield 는 쓰레드에게 주어진 Time Slice 를 즉시 반납하도록 하며, 스케줄러에게 쓰레드가 어떤 일을 할지 알아서 정해달라고 요청을 하게 된다. 이전에 Lock 구현 이론에서 설명한 것 중 두번째인, '랜덤 시간 후 복귀' 메타가 바로 sleep 이다. 이전 글에서 SpinLock 을 이용해 '무작정 기다리는..

[C++ 게임 서버] 1-7. SpinLock 핵심 : 1. SpinLock 은 Lock 을 잡기 위해 '무작정 기다리는' 방식이다. 2. SpinLock 은 atomic 변수와 lock, unlock 함수로 쉽게 만들 수 있다. compare_exchange_strong 함수를 기억하자. 3. SpinLock 은 불필요한 CPU clock 을 낭비하지만, 때로는 컨텍스트 스위칭에 드는 것보다 비용이 저렴할 수도 있다. 이제 본격적으로 SpinLock 을 구현해 보자. SpinLock 은 간단하다. 그냥 Lock 을 잡을 때까지 무작정 While 문을 돌면서 기다리도록 만들 것이다 🤣 간단하게 생각하면 아래와 같이 구현하겠지만... int sum = 0; class SpinLock { private: b..

[C++ 게임 서버] 1-6. Lock 구현 이론 핵심 : 1. Lock 을 구현하는 방식은 크게 세 가지로 나뉜다. 무작정 기다리기, 잠시 후 돌아오기, 깨워줄 때까지 기다리기 2. SpinLock 을 사용하여 무작정 기다리거나, 어느 정도 Sleep 을 하고 난 후 Lock 을 다시 잡으려고 시도할 수도 있다. 3. 혹은 다른 일을 하다가, Lock 을 잡을 수 있게 되는 상태가 되었을 때 노티를 달라고 요청할 수도 있다. 예를 들어 Condition Variable 이 있다. 이 과정에서 컨텍스트 스위칭이 일어날 수 있다! 본격적으로 Lock 을 구현하기 전, Lock 을 구현하는 일반적인 방식 3 가지에 대해 먼저 소개하고 가도록 하겠다. 사실 내용은 간단하다. Lock 이 이미 걸린 상태라고 할..

[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