KoreanFoodie's Study
[C++ 게임 서버] 6-4. JobQueue #3 본문
[C++ 게임 서버] 6-4. JobQueue #3
핵심 :
1. 이번 글에서는 JobSerializer 를 이용해, 람다 함수를 SharedPtr 로 만들어 Push 해 줌으로써 좀 더 간편하게 Job 관리하는 방법을 알아보자.
2. 람다와 SharedPtr 를 함께 쓴다고 해서 Memory Leak 이 일어나지는 않는다. 만약 문제가 있다면 람다와 SharedPtr 를 함께 사용해서 문제가 생기는 것이 아니라, 기본적인 설계가 잘못된 것이다.
이번 시간에는 기존에 만들었던 Job 을 좀 더 간편하게 사용할 수 있도록 구조를 약간 수정해 보자. 먼저 Job 을 아래와 같이 ServerCore 에 만들어줄 것이다.
/*---------
Job
----------*/
using CallbackType = std::function<void()>;
class Job
{
public:
Job(CallbackType&& callback) : _callback(std::move(callback))
{
}
template<typename T, typename Ret, typename... Args>
Job(shared_ptr<T> owner, Ret(T::* memFunc)(Args...), Args&&... args)
{
_callback = [owner, memFunc, args...]()
{
(owner.get()->*memFunc)(args...);
};
}
void Execute()
{
_callback();
}
private:
CallbackType _callback;
};
잘 보면, Job 은 그냥 Callback 함수를 하나 받을 수도 있고, 특정 클래스 객체의 멤버함수를 받을 수도 있음을 알 수 있다.
Execute 를 하면, 콜백함수가 불리게 된다.
JobQueue 클래스는 아래와 같이 생겼다 :
class JobQueue
{
public:
void Push(JobRef job)
{
WRITE_LOCK;
_jobs.push(job);
}
JobRef Pop()
{
WRITE_LOCK;
if (_jobs.empty())
return nullptr;
JobRef ret = _jobs.front();
_jobs.pop();
return ret;
}
private:
USE_LOCK;
queue<JobRef> _jobs;
};
사실 이번 글의 핵심은 아래 소개할 JobSerializer 이다.
JobSerializer 는, 이전에 우리가 PushJob 을 했던 부분을 좀더 API 화 시킨 느낌이다. 코드를 보면...
/*-----------------
JobSerializer
------------------*/
class JobSerializer : public enable_shared_from_this<JobSerializer>
{
public:
void PushJob(CallbackType&& callback)
{
auto job = ObjectPool<Job>::MakeShared(std::move(callback));
_jobQueue.Push(job);
}
template<typename T, typename Ret, typename... Args>
void PushJob(Ret(T::*memFunc)(Args...), Args... args)
{
shared_ptr<T> owner = static_pointer_cast<T>(shared_from_this());
auto job = ObjectPool<Job>::MakeShared(owner, memFunc, std::forward<Args>(args)...);
_jobQueue.Push(job);
}
virtual void FlushJob() abstract;
protected:
JobQueue _jobQueue;
};
Job 객체를, ObjectPool 을 이용해 SharedPtr 형태로 JobQueue 에 넣어 주고 있음을 확인할 수 있다.
뭔가 신기하다고 느껴질 수 있는 부분인데... 람다 함수를 SharedPtr 로 넣어줌으로써 Dangling Pointer 이슈 등을 해결하고 있다.
FlushJob 의 경우, 이전에 Room 클래스에서 했던 것처럼 while 문을 돌면서 JobQueue 의 Job 을 하나씩 꺼내 Execute 를 호출해 줄 것이다. 😉
그럼 게임 서버는 ClientPacketHandler 에서 아래와 같이 Job 을 간편하게 Push 해주게 된다! 😊
bool Handle_C_CHAT(PacketSessionRef& session, Protocol::C_CHAT& pkt)
{
std::cout << pkt.msg() << endl;
Protocol::S_CHAT chatPkt;
chatPkt.set_msg(pkt.msg());
auto sendBuffer = ClientPacketHandler::MakeSendBuffer(chatPkt);
GRoom->PushJob(&Room::Broadcast, sendBuffer);
return true;
}
참, 검색을 하다 보면 람다와 SharedPtr 를 같이 쓰면 Memory Leak 이 발생한다는 그런 얘기가 떠도는데... 이는 사실이 아니다!
뭐.. 만약에 다음과 같은 클래스는 순환 참조가 발생해 Memory Leak 이 발생할 것이다 :
class Knight : public enable_shared_from_this<Knight>
{
public:
Knight() : _knight(shared_from_this())
{
}
protected:
shared_ptr<Knight> _knight;
};
만약 위와 같은 코드가 있다면... 참조가 해제가 안되어 Memory Leak 이 발생할 것이다. 😅
람다와 SharedPtr 를 같이 사용한다고 memory leak 이 발생하는 것은 아니니, 문제가 있다면 설계가 이상하지 않은지를 되짚어보자! 😁
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 6-6. JobQueue #5 (0) | 2023.12.20 |
---|---|
[C++ 게임 서버] 6-5. JobQueue #4 (0) | 2023.12.20 |
[C++ 게임 서버] 6-3. JobQueue #2 (0) | 2023.12.20 |
[C++ 게임 서버] 6-2. JobQueue #1 (1) | 2023.12.19 |
[C++ 게임 서버] 6-1. 채팅 실습 (0) | 2023.12.19 |