메인 루프의 이해

클라이언트 메인 루프

ProudNet의 클라이언트는 polling 방식으로 메시지 수신이나 이벤트가 발생했을 때 RMI 호출 콜백이나 이벤트 핸들러 콜백은 게임 클라이언트 메인 루프에서 특정 함수를 호출했을 때의 스레드에서만 콜백됩니다.

주로 게임 클라이언트가 빠르게 도는 루프를 갖고있기 때문에 의도치 않은 스레드 작동이 나타나는데, 위와 같은 방법으로 설계하면 클라이언트 개발자는 복잡한 스레드 프로그래밍에 대한 부담이 줄어들게 됩니다.

서버 메인 루프

- 수신 및 이벤트 콜백

게임 서버는 모든 CPU를 활용하고, DB를 접근하는 동안에도 다른 클라이언트에 대한 수신 처리를 하기 위해 스레드 풀링(Thread pooling)를 활용하는데 ProudNet 또한 이 방식으로 작동하기 때문에 다음과 같은 특징들을 가지고 있습니다.

  • 서버 시작 시, 별도의 스레드 풀(Thread pool)를 생성합니다.

  • 게임 클라이언트는 일정 시간마다 수신 처리를 위한 함수를 호출해야 쌓인 수신 메시지를 처리하지만, 서버는 이런 함수 호출이 필요하지 않습니다.

  • 서버에서 이벤트나 RMI 수신이 발생하면 Server가 보유한 스레드 풀에서 콜백됩니다.

  • 같은 클라이언트에 대한 이벤트나 RMI 수신은 동시에 2개 이상 스레드에서 RMI 수신이 콜백되지 않지만 ProudNet은 반드시 도착 순서대로 RMI 수신이 콜백됩니다. 물론 서로 다른 클라이언트에 대한 이벤트, RMI 는 동시다발적으로 콜백이 발생합니다.

  • 모든 스레드가 콜백 루틴이 실행되고 있는 동안 RMI 나 이벤트가 발생할 땐 당장 콜백이 일어나지 않지만, 콜백 루틴이 완료되는 스레드가 있을 때까진 해당 콜백은 대기됩니다.

  • 사용자가 구현한 콜백 루틴의 실행 시간이 길어도 네트워크 통신에는 장애가 일어나지 않기 때문에, 이를 위한 별도의 스레드 풀을 자체 구현할 필요가 없습니다.

다음은 클라이언트 A,B,C가 서버에 수용된 상태에서 각각 RMI 또는 이벤트로 인하여 Server내의 queue에서 대기 중인 상태를 표현한 이미지 입니다.

A1,A2,A3 -> 클라이언트 A에 대한 이벤트 또는 RMI B1,B2,B3 -> 클라이언트 B에 대한 이벤트 또는 RMI 스레드 풀의 스레드는 총 2개입니다.

이때 규칙에 따라 다음과 같이 실행됩니다.

  • A1,A2,A3가 동시에 실행되지는 않습니다.

  • B1,B2,B3 와 C1,C2,C3 도 마찬가지로 동시에 실행되지는 않습니다.

  • A1,A2,A3 중 하나와 B1,B2,B3 중 하나, C1,C2,C3 중 하나는 동시에 실행될 수 있습니다.

  • 스레드 풀의 스레드 갯수가 2개 뿐이므로 A,B,C 중 2개가 선별되어 콜백되지만, 콜백 루틴이 먼저 완료된 스레드가 선별되지 못한 클라이언트의 RMI 혹은 이벤트 콜백을 수행합니다.

- 타이머 콜백

게임 클라이언트처럼 게임 서버 또한 일정 시간마다 뭔가를 처리하고자 할 수 있습니다. 이러한 경우 아래와 같은 단순한 루프를 가지는 방법이 있습니다.

while(1)
{
    do_something(); // 월드 천이 연산을 시행
    Sleep(1); // 일정 시간 대기
}

Proud.CTimerThreadProud.CTimerQueue 를 이용하여 별도 스레드에서 위 루프를 실행하게 만드는 방법도 있습니다. 하지만 서버 스레드 풀에서 직접 사용자가 정의한 타이머 함수가 호출되게 하는 방법을 추천합니다.

예를 들어 서버가 1개의 스레드만 가질 경우, 타이머 함수와 이벤트 콜백이 같은 스레드이면 critical section 접근 횟수를 절약할 수도 있기 때문입니다. ProudNet은 이러한 기능 또한 내장하고 있으니 참고하시기 바랍니다.

Last updated