월 66,000원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
lock lock-free 성능 차이가 없다는 부분에 대하여
강의 하실때 lock방식과 lock-free방식에 성능 차이가 없다는 것처럼 이야기 하셔서, 왜 성능 차이가 없을까 라는 것을 생각 해 보면, 주로 성능 차이가 어디에서 오냐를 먼저 고민해 봐야 할 것입니다. 저의 생각으로는, 정상적인 상황에서 Context switching으로 인해 커널 - 유저 모드 전환에서 큰 비용이 발생한다고 생각이 됩니다. 전통적인 Event 방식이 대표적인 예라고 생각합니다. 작업하신 lock-free 방식을 볼때마다 spinlock이 계속 떠올랐습니다. 결국 User mode에서 polling하고 있는 것이 겠지요. 어디서 주워 듣기로는 요즘 lock들도 커널 모드로 바로 들어가 일정시간 잠들지 않고, user mode에서 3000번, 5000번 정도 while을 돌면서 소유권을 얻을 수 있는지 확인한다고 합니다. 그 횟수동안 얻지 못하면 sleep 되는 식으로 알고 있습니다. 결국 빠르게 소유권이 전환되는 상황이라면 user mode에서만 작업이 발생하기 때문에 큰 overhead가 없다고 볼 수 있을 것 같습니다. 이러한 이유로 두 방식의 성능 차이가 거의 없다고 보는데 다른 분들의 의견이 궁금합니다.
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
실무에서도 TCHAR 형을 쓰나요?
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요. 좋은 강의 감사드립니다(_ _) 강의에서 보통 실무에서 문자셋을 약속을 하고 쓴다고 하셨는데 THCAR를 쓰게 되면 딱히 고려하지 않아도 될 부분 같아 보입니다. THCAR를 안 쓴다면 그 이유가 있을까요? 모든 문자열 함수를 사용할 때 tchar를 지원하는 함수를 쓰게 돼서 가독성이 떨어져서 일까요? 혹은 바이트 단위 연산을 하게 될때 가변적인 문자셋을 가정하고 코딩하는게 힘들어서 일까요?
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
패킷 리시브버퍼 관련 질문이 있습니다.
패킷 리시브 할 때 WSARecv의 작동에 의문이 생겨 질문을 남겨봅니다. [Size][ID][Hello World] 라는 패킷을 수신하다고 가정해봤을 때 Session::ProcessRecv 가 호출된 시점에서 [Size][ID][Hello] 까지의 데이터를 받았다고 가정하면 PacketSession::OnRecv에서 dataSize가 header.size보다 작기 때문에 0바이트를 Return 해 버리고 끝납니다. 이러면 RecvBuffer의 _writePos, _readPos에는 아무 변화 없이 다시 RegisterRecv를 걸어버리는데요 다음 RegisterRecv (WSARecv) 를 걸 때 , 읽다만 데이터의 시작 지점 부터 다시 읽어버리는게 아닌가 싶어서 여쭤봅니다 첫 번째 ProcessRecv를 받았을 때 RecvBuffer이 다음과 같다면 두 번째 ProcessRecv에서는 남은 "World" 데이터가 RecvBuffer 뒤쪽에 쌓이게 되는지, 아니면 변하지 않은 _writepos 위치 때문에 앞에서 부터 다시 작성되게 되는지 의문이 생겨 여쭤봅니다. 제 생각 중 만약 전체 바이트가 아닌 일부 바이트만 수신되는 위와 같은 과정에서 WSARecv는 2번 따로 따로 호출된다고 이해한게 맞으며, RecvBuffer에 문제없이 데이터가 이어서 들어가게 되나요?
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
변수 초기화
CheckCycle함수에서 visitedCount=0을 안해줘도 괜찮나요?
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
listen function의 백로그와 Accet
Listen이랑 Accet를 인터넷에서 찾아보고 제가 이해한게 맞는지 확인차 질문드립니다. Listen함수를 호출 하면 외부에서 연결을 시도할경우 백로그 큐에 삽입된다. 그후 Accet함수로 백로그큐에서 받은 소켓을 꺼내온다고 보면될까요?
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
시작 전에 질문
안녕하세요. 언리얼 파트1을 수강하고 이제 파트3 자료구조를 수강하고 있는 수강생입니다.강좌 관련 내용은 아닌데 .. 궁금한 사항이 있습니다.TCP/IP 소캣 통신정도는 간단히 복습해보고 다음주 쯤에 이 파트4 서버강의를 들을까 하는데요.커리큘럼을 어떻게할지 고민이 됩니다.듣기전에 유니티 로드맵을 쭉 따라가서 큰그림을 그려보는게 이 강의를 이해하는데 도움이 될지.아니면 이 강의를 듣고 유니티 로드맵을 들으면 코어한 부분의 지식이 있어서 유니티가 서버가 이해가 더 잘될지..둘 다 해보는게 가장 빠르겠지만 경험이 더 많으신 분의 의견이 궁금합니다.그리고 파트2 리뉴얼 강의는 혹시 언제쯤 될지 계획이 있으시면 알려주시면 커리큘럼(?)을 고민하는데 도움이 될거 같습니다.항상 좋은 강의 감사합니다.
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
name을 왜 자신의 클래스 이름으로 하나요?
lockId는 모든 lock마다 1개씩 있어야 하는 것 아닌가요? 그런데 제 생각으로는 name을 자신의 클래스 이름으로 하면 같은 클래스 이름을 가진 다수의 lock이 생기는데, 모든 lock마다 lockId가 1개씩 생길 수 없습니다. 예를 들어 A라는 클래스의 객체가 WriteLock을 걸고, WriteLock을 풀지 않은채로 ReadLock을 걸었습니다. 그러면 1번에서 건 WriteLock은 PushLock 함수 내에서 A라는 name으로 새로운 Id인 0번 Id를 받게 될 것입니다. 그 후 2번에서 건 ReadLock은 PushLock 함수 내에서 A라는 name을 찾은 결과 이미 0번 Id를 가지고 있었기 때문에 또 다시 0번 Id를 받게됩니다. 그러면 WriteLock과 ReadLock은 서로 다른 lock인데도 똑같이 0번 Id를 가지게 됩니다. 위의 생각에서 잘못된 부분이 무엇인가요? 혹은 제가 놓치고 있는 부분이 무엇인가요?
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Condition Variable 질문입니다.
//Producer while(true) { { unique_lock<mutex> lock(m); q.push(100); } cv.notify_one(); } //Consumer while(true) { unique_lock<mutex> lock(m); cv.wait(lock,[](){return q.empty()==false;}); { int32 data = q.front(); q.pop(); cout<<data<<endl; } } 1. Producer에서 cv.notify_one(); 을 한 뒤 Consumer의 cv.wait가 깨어나기 전에 Producer가 unique_lock<mutex> lock(m); 을 걸면 pop을 하지 못하고 queue에 데이터가 연속으로 쌓이는 경우가 있나요? 2. 디버깅을 찍어보니 cv.wait가 처음 수행되었을 때는 cv.notify_one() 없이도 그냥 깨어나던데 cv.wait는 notify를 해줬을 경우만 깨어나지 않나요? ex) 스레드 Producer, Consumer을 모두 실행 직후 최초 q.push 이후 notify_one 없이 wait가 깨어나 data를 cout 함. 3. notify_one 해줬을 때 cv.wait의 [](){return q.empty()==false;} 조건이 충족되지 않았을 때 대기를 하는데(queue에 아무것도 없는 상태) 이때, queue 에 push를 해주면 별도의 반복 notify_one 작업 없이 바로 깨어나나요? 디버깅을 해보니 notify_one을 걸고 조건 실패하여 대기중일때 queue에 push를 해주니 꺠어나는것 같았습니다. notify_one은 1회성으로 바로 리턴하고 끝인줄 알았는데 Count가 하나 증가하여 queue에 data가 쌓일때까지 대기하는? 개념인지 궁금합니다.
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
다음 강의는 언제쯤 나올까요?
2달 전에 올라온 공지가 마지막이네요. 루키스님 근황도 듣고 싶습니다. 다음 강의 기다리다가 죽을 것 같습니다ㅠ,ㅠ
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
TCP 서버 질문입니다.
Client가 100바이트씩 10번 보내어 서버가 1000바이트씩 읽는 것에대해서는 이해를 했는데요. (데이터의 경계가 없기 떄문) 서버의 recv 함수의 3번째 인자로 1000바이트를 설정할 경우 1000바이트를 TCP recv 버퍼에서 응용프로그램 버퍼에 복사할때까지 블로킹 되는 걸로 알고있는데 recv 함수의 3번째 인자를 1000으로 설정했어도 리턴값이 50바이트같이 1000보다 작은 숫자가 나올 수 있나요? 아니면 계속 1000바이트를 읽을때까지 블로킹 되는 것인가요?
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Connect 함수에 대해 질문이 있습니다.
안녕하세요 영상 4분쯤에 나오는 RegisterConnect 에 대해 질문이 생겨서 남겨봅니다. 먼저 제가 이해한 바를 정리해 보면 클라이언트 -> 서버 연결의 경우에는, DummyClient 입장에서 서버에 붙어야 하기에 RegisterConnect를 호출해 주었고 Server입장에서는 Listener에서 ProcessConnect를 바로 호출해 준다고 이해했습니다. 이전에 서버 -> 서버간의 연결이 분산 서버 환경에서 일어날 수 있다고 말씀주셨고 영상 4분쯤(RegisterConnect 함수 제작 중)에 서버와 서버를 연결할 경우, 상대방이 나한태 붙는 개념이라, ServiceType이 Client 인 경우에만 RegisterConnect 를 호출할 수 있다 설명해 주셨는데 이 부분이 잘 이해가 가지 않습니다. 만약 서버 -> 서버의 연결의 필요해 지는 경우에는, 서버입장에서 ClientService 를 생성해서 대상 서버에 RegisterConnect를 호출해야 하는 건가요? 아니면 따로 추가 함수 제작이 필요한 건가요? ========================== 죄송합니다 추가로 질문 하나 더 남겨봅니다. 저희가 사용하고 있는 IocpEvent는 OVERLAPPED를 상속받아 메모리 처음 시작 부분에 OVERLAPPED 구조체 정보가 들어있어서 IocpEvent <-> OVERLAPPED 간의 타입캐스팅이 가능했었는데, 이런 방식의 기믹은 뭐라고 하며, virtual 함수가 없는 이상 언제나 안전하게 사용할 수 있는 방법인가요? https://stackoverflow.com/questions/22404423/c-pod-struct-inheritance-are-there-any-guarantees-about-the-memory-layout-of 관련 내용을 찾다보니 메모리 레이아웃이 보장되지 않는다는 내용이 보여서 여쭤봅니다.
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
typecast 강의 관련 질문입니다.
template<typename T, typename... U> struct Length<TypeList<T, U...>> { enum { value = 1 + Length<TypeList<U...>>::value }; }; int32 len1 = Length<TypeList<Mage, Knight>>::value; 위 코드에서 Length<TypeList<Mage, Knight>>가 처음 호출되게 되면 T = Mage, U = Knight가 되어 value = 1(Mage) + Length<(Knight=>)TypeList<U...>>::value가 된다고 알고있습니다. 그런데 template<typename T, typename... U> struct Length<TypeList<T, U...>> { } 로 Length 구조체 자체가 T,와 가변인자 U를 받는데 어떻게 value = 1 + Length<TypeList<U...>>::value 에서 가변인자 U...하나만 받을 수 있는지 궁금합니다. 재귀호출이 되려면 Length<TypeList<Mage, Knight>>::value; 처럼 TypeList에 T에 해당하는 것과 U에 해당하는것을 모두 넣어야하지 않나요?
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
가시성 질문드립니다
조금 개념이 헷갈리는데요. 가시성의 개념이 어떤 쓰레드에서 공유변수의 값 하나를 수정했을 때, 다른 쓰레드가 해당 변수를 읽어들일 때 그 수정된 값을 읽어들인다는 보장이 없다. (캐시의 문제) 여기서 원자적 연산을 진행하면 동일 객체에 대해서 동일한 수정 순서를 관찰한다고 했는데, 항상 수정된 이후의 값이 관찰되므로 가시성이 해결된다고 보면되나요? 17분 50초쯤에 동일한 수정순서를 관찰한다해서 가시성문제가 여전히 해결된다고 하지 않는다고 하셨는데 34분 37초쯤에서는 atomic의 기본연산이 seq_cst정책이 디폴트이고 가시성 문제와 코드 재배치문제도 바로 해결된다고 하시는데 어떤말이 맞는지 궁금합니다
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
volatile질문
2분55초에 나오는 bool 변수 ready를 volatile로 둔 이유가 컴파일러의 최적화로 인해 메인함수안의 ready = false 선언 하고 쓰레드 생성 후 ready = true를 무시하고 바로 ready에 true에 넣는것을 막기 위함인걸까요?
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
메모리풀 #1 강의 질문입니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 메모리 헤더 구조체의 static void* AttachHeader(MemoryHeader* header, int32 size) 함수에서 [MemoryHeader][Data] 와 같은 구조라고 하셨는데 32바이트를 할당받은 메모리 풀일 경우 [<----32바이트----->] [MemoryHeader][Data] 인 구조가 맞나요? 또한 MemoryHeader만큼 +1하여 [MemoryHeader]->[Data] Data를 가르킨채로 해당 영역을 리턴하면 컴파일러가 알아서 해당 메모리에 저장하고 사용하는 건가요? Data 영역을 리턴한 이후로 작업은 별도로 수행하지 않아서 문의 드립니다.
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
int를 int32로 선언하는 이유?
int 형은 32/64비트 환경 모두 4Byte로 알고있습니다. 그런데 선생님 소스에는 int32로 선언하시던데 그 이유가 궁금합니다.!
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
condition_variable 파트 질문입니다.
강사님 제가 이해한 부분이 맞는지 궁금합니다. 제가 처음에 이해한 부분입니다. 1. Consumer에서 먼저 잡은 lock을 한 경우 unlock 후 대기, Producer에서 lock을 하고 push한 후, cv.notify_one()을 실행한다. 2. Consumer에서 먼저 잡은 경우는 cv.wait()에서 lock을 걸고 진행하고, 늦게 잡은 경우도 unique_lock에서 lock을 건다. 3. Consumer에서 끝나면 Producer는 이미 계속 lock을 걸려고 하고 있으므로, 1, 2번을 반복한다. 위의 경우라면 큐의 사이즈가 0 1 0 1 0 1...이 반복되서 결과 값이 0 0 0 0... 이 된다고 생각했습니다. 하지만 결과 값은 확 늘었다가 1씩 줄어들다가 또 확 늘었다가 1씩 줄어들다를 반복하였습니다.(결과적으로 몇천, 몇만, 몇십만까지 증가하였습니다.) 코드의 첫 번째 주석대로 Producer를 기준으로 큐의 사이즈 값을 출력하니 0 0 0 0... 까지는 아니지만 모든 수가 4미만으로 나옵니다. Producer에서 lock을 해제할 경우 Consumer에서 접근이 가능하지만 조건이 걸려있어 0미만으로는 가지 않을 것이고, Consumer에서 lock을 해제할 경우 Producer에서 여러 번 값을 넣는 경우라면 이해는 갑니다. 위 경우를 생각하여 Producer에게 sleep_for 함수를 적용해서 코드의 두 번째 주석을 넣고 실행하니 기존 답에서 모든 수가 4미만으로 나왔습니다. 말이 길었습니다.. 질문입니다. 제가 이해한 경우가 맞는지 아니면 멀티쓰레드 쪽의 개념이 포괄적으로 이해해야 될까요? 강사님이 말씀하신대로 뇌가 말랑해지기위해 노력중입니다. mutex m; queue<int32> q; condition_variable cv; void Producer() { while (true) { { unique_lock<mutex> lock(m); q.push(100); } cv.notify_one(); //cout << q.size() << endl; //this_thread::sleep_for(0.001ms); } } void Consumer() { while (true) { unique_lock<mutex> lock(m); cv.wait(lock, []() {return q.empty() == false; }); int32 data = q.front(); q.pop(); cout << q.size() << endl; } }
- 해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
SpinLock 질문입니다.
CAS 의사코드 보면서 든 의문입니다. _locked와 expected가 같을 때 expected = _locked을 하는 작업이라든가 필요없을 것 같은 작업들이 있는 것 같아 아래와 같이 작성하여보았습니다. 작동은 똑같이하는데 문제가 생길 것이 있을까요? bool check(bool& locked) { if (!locked) { return locked = true; } else { return false; } } class SpinLock { public: void lock() { /*bool expected = false; bool desired = true;*/ // CAS (Compare-And-Swap) // 의사 코드 /*if (_locked == expected) { expected = _locked; _locked = desired; return true; } else { expected = _locked; return false; }*/ /*while (_locked.compare_exchange_strong(expected, desired) == false) { expected = false; }*/ while (check(_locked)) {} } void unlock() { _locked = false; } private: bool _locked = false; //atomic<bool> _locked = false; };
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
TypeConversion 내부 질문드립니다
항상 좋은 강의 감사드립니다. 1. if (Conversion<const FromType*, const ToType*>::exist) 이 부분에서왜 포인터를 사용했는지 궁금합니다. 2. TypeConversion은 결국 많은 템플릿을 생성하게 되는데 그럴 경우 메모리에 문제가 없는지 궁금합니다. 3.Conversion 의 경우 컴파일러가 비슷하다고 판단 할경우 From을 To로 변환을 하고 결국 그걸 이용해 변경 여부를 판단하게 되는데 컴파일러 입장에서 비슷할 경우 변환을 시키지 않는 것이 이득일텐데 굳이 변환을 하는 원리가 궁금합니다. 4. 마지막으로 struct로 만드실 때와 class로 만들 실 때의 기준이 있으신가요? template 흑마법 정말 잘 배웠습니다 핵? 편법같은 느낌이 있어 관심이 많이 가게 되었습니다. 추후 강의 기대하겠습니다!
- 미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
CoreTLS.h에서 uint32 사용
강의 내용을 그대로 따라했는데요? CoreTLS.h의 LThreadId를 선언하는 부분에서 강의와 다른 상황이 발생했습니다. CoreTLS.h의 LThreadId를 선언하는 부분에서 어떻게 Types.h를 포함해주지 않고 uint32를 사용할 수 있나요? #include "Types.h"를 해주면 잘 됩니다.