우선 기존코드의 문제점을 해결
class GameSession : public PacketSession
{
public:
	~GameSession()
	{
		cout << "~GameSession" << endl;
	}
	virtual void OnConnected() override;
	virtual void OnDisconnected() override;
	virtual void OnRecvPacket(BYTE* buffer, int32 len) override;
	virtual void OnSend(int32 len) override;
public:
    // Player정보를 shared_ptr로 관리중인데
	Vector<PlayerRef> _players;
class Player
{
public:
	uint64					playerId = 0;
	string					name;
	Protocol::PlayerType	type = Protocol::PLAYER_TYPE_NONE;
    // Player자체에서도 GameSession을 shared_ptr로 관리중
	GameSessionRef			ownerSession; // Cycle
};
void GameSession::OnDisconnected()
{
	GSessionManager.Remove(static_pointer_cast<GameSession>(shared_from_this()));
	if (_currentPlayer)
	{
		if (auto room = _room.lock())
			room->DoAsync(&Room::Leave, _currentPlayer);
	}
	_currentPlayer = nullptr;
	_players.clear();
}
JobTimer? -> 예를들어 설명하겠다. 어떤 캐릭터가 스킬을 써서 5초후 발동된다고 가정해보자 그럼 그 캐릭터의 오브젝트에서 5초를 카운트해야할까? 만약 캐릭터가 1000개라면 카운터가 1000개가 있어야하나??
JobTimer를 두어서 카운터를 전역으로 관리해보자
int main()
{
	GRoom->DoTimer(1000, [] { cout << "Hello 1000" << endl; });
	GRoom->DoTimer(2000, [] { cout << "Hello 2000" << endl; });
	GRoom->DoTimer(3000, [] { cout << "Hello 3000" << endl; });
class JobQueue : public enable_shared_from_this<JobQueue>
{
public:
	// ...
	void DoTimer(uint64 tickAfter, CallbackType&& callback)
	{
		JobRef job = ObjectPool<Job>::MakeShared(std::move(callback));
		GJobTimer->Reserve(tickAfter, shared_from_this(), job);
	}
void JobTimer::Reserve(uint64 tickAfter, weak_ptr<JobQueue> owner, JobRef job)
{
	const uint64 executeTick = ::GetTickCount64() + tickAfter;
	JobData* jobData = ObjectPool<JobData>::Pop(owner, job);
	WRITE_LOCK;
	_items.push(TimerItem{ executeTick, jobData });
}
void JobTimer::Distribute(uint64 now)
{
	// 한 번에 1 쓰레드만 통과
	if (_distributing.exchange(true) == true)
		return;
	Vector<TimerItem> items;
	{
		WRITE_LOCK;
		while (_items.empty() == false)
		{
			const TimerItem& timerItem = _items.top();
			if (now < timerItem.executeTick)
				break;
			items.push_back(timerItem);
			_items.pop();
		}
	}
	for (TimerItem& item : items)
	{
		if (JobQueueRef owner = item.jobData->owner.lock())
			owner->Push(item.jobData->job);
		ObjectPool<JobData>::Push(item.jobData);
	}
	// 끝났으면 풀어준다
	_distributing.store(false);
}
void JobTimer::Clear()
{
	WRITE_LOCK;
	while (_items.empty() == false)
	{
		const TimerItem& timerItem = _items.top();
		ObjectPool<JobData>::Push(timerItem.jobData);
		_items.pop();
	}
}