[c++] 스레드 풀링

TNT·2024년 8월 10일

c++ 기초

목록 보기
12/19

보통 멀티스레드로 서버를 개발 해야하는 상황이있는데

멀티스레드로 서버를 개발하는경우

  1. 보통 mmo 게임 에서 쓴다고 많이 보는데 이유는 프로세스당 로딩해야하는 게임정보의 용량이 매우 클때 사용된다.
  2. 서버 한대의 프로세스가 여러 cpu의 연산량을 동원해야할정도로 많이 써야할때
  3. 코루틴이나 비동기 함수 못쓰는상황에서 디바이스 타임 발생할때
  4. 서버 인스턴스를 서버 기기당 하나만 두어야 할 때
  5. 서로 다른 방이 같은 메모리 공간을 엑세스 해야할때

다 상황을 보면 무언가 한가지 행동을 할때 추가적으로 무언갈 해야하는 동작들이다


그래서 싱글 스레드 서버 와 코드를 비교해보면 차이를 쉽게 알수있다.

예시

싱글 스레드 경우

class MyGameServer
{
  class Room
  {
    String m_roomName;
    List<Player> m_players;
    List<Character> m_characters;
    class MyGameServer
  }
  map<PlayerID, shared_ptr<Room>> m_roomList;
  String m_serverName;
}

멀티 스레드 경우

class MyGameServer
{
  class Room
  {
    // 각 게임방 안의 데이터들을 보호한다. 
    Critical Section m_critSec; 
    String m_roomName;
    List<Player> m_players; 
    List<Character> m_characters;
  }
    map<PlayerID, shared_ptr<Room> > m_roomList;
    String m_serverName;
    // 서버 메인과 룸 목록을 보호한다.
    // 단 방 안의 데이터는 보호하지 않는다. 
    Critical Section m_critSec;
}

각 룸에서는 뮤텍스를 가지는데 게임 서버 메인 자체도 뮤텍스를 하나 가진다.
그래서 멀티스레드 코드를 보면 룸 목록이나 룸의 공통 데이터는 보호 하짐나 자기가 가지고있는 방 안의 뮤텍스가
보호하는 데이터까지는 보호 하지 않는다.

각 플레이어 행동에 대한 처리는 각 방을 잠근후에 하는데
전 게시글에서도 나온 내용 멀티 스레드 이야 할때 나온 거지만
멀티스레드 게임 서버를 만들 때 크게 주의할점 시리얼 병목과 교착 상태 를 조심해야한다.
특히 파일 엑세스할 때 자주 잠그는 뮤텍스를 잠근채로 엑세스 하는 경우 성능 저하가 자부 발생한다.

스레드 풀링

멀티스레드 모델의 게임서버를 개발 할경우 스레드는 몇개 만들고, 뭐할지 생각하는데
가장 쉬운방법은 그냥 하나의 클라이언트에 한개의 스레드를 배정 하는것이 쉬운 방식이다 고정으로 그 클라이언트에 머물면서 일처리하고 없으땐 쉬고 나가면 다른곳으로가고 하지만 스레드가 개수가 많아지면 문제가 발생한다.

각 스레드는 호출 스택을 가지고 있는데 이것의 크기는 작게는 수십 키로 바이트에서 수메가 바이트까지 이른다.
스레드가 5000개고 스레드마다 호출스택 1메가 이면 메모리는 호출하는데만 5000메가 바이트를 소모 하게 된다.

또 다른 문제는 컨텍스트 스위치 현상이 발생한다. 스레드가 하는일이 많이 없으면 상관 없지만 게임 서비스는
클라이언트와 서버가 통신하는 횟수가 많기 떄문에 문제가 발생한다. 그래서
이 스레드 풀링을 이용해서 작업을 하는것이다.

유니티 공부 했던 사람이면 오브젝트 풀링 느낌이다.

그러면 스레드가 6개 있다고 가정하면 작업 하는게 10개 있으면 6개는 작업하고 4개는 대기 하고 있다가 앞 작업이 끝나면 이어서 동작 하는것이다.

똑같이 스레드 6개가 있고 cpu는 8코어 인경우 최대 6개 스레드가 병령로 실행하는데
cpu 8개중에 2개는 배정을 못받는가 그러면 최대로 cpu 처리 능력의 6/8만 사용되는것이다.

그러면 반대로 cpu개수 보다 스레드가 많으면??

똑같이 cpu 8코어 이고 스레드는 10개면 8개스레드가 cpu에 배정된다 나머지 2개 스레드는 중복으로 배정 받아야한다.
그러면 cpu 입장에선 2가지 처리를 같이 해야한다. 이때 컨텍스트 스위치를 하면서 두 스레드를 실행 한다.

스레드가 노는 시간이 많다면 더 배정해도 괜찮다.
이번엔 스레드가 cpu와 동일하게 8개 가지고 있다면 스레드가 쉬는 경우가 있는데 이 경우 대기하는 시간을 생겨서 연산량이 떨어진다.

그래서 이왕 노는 시간이 생기면 일처리를 더 넣어주면 좋다. 그래서 총 시간의 1/4가 연산이고 3/4쉬는 시간이면 3배 해서 넣어주면 적절 하다.

스레드 개수에 다음 규칙을 도출할 수 있다.
1. 서버의 주 역활이 cpu 연산만 하는 스레드 라면 (디바이스 타임이 없다면) 스레드 풀의 스레드 개수는 서버의 cpu 개수와 동일하게 잡아도 충분합니다.
2. 서버에서 데이터 베이스나 파일 등 다른 것에 엑세스하면서 디바이스 타임이 발생할 때는 스레드 개수는 cpu 개수보다 많아야한다.( 멈추는 현상이 없게 )

참고 도서
게임서버 프로그래밍 교과서

profile
개발

0개의 댓글