방프리

서버에서의 동기화와 비동기화 본문

Server&Network/TCP&IP

서버에서의 동기화와 비동기화

방프리 2020. 1. 8. 05:32

게임 서버의 로직과 클라이언트의 로직은 얼핏 보면 비슷하지만 다릅니다? 

게임 서버와 클라이언트 둘 다 객체의 행동에 대한 것을 코드로 짜지만 

클라이언트에서는 객체의 행동 및 행동에 대한 검사를 하지만 

서버에서는 객체의 상태 공유 및 아이템 등의 검사, 유저들의 정보를 저장하는 등의 역할을 맡습니다.

다만 크게 다른 점이 있다면 게임 서버 여러 명을 혼자 상대하는 1 대 다수의 역할을 하고

클라이언트는 1 대 1의 역할을 합니다. 

그러다보니 서버는 어쩔 수 없이 쓰레드를 사용할 수 밖에 없죠 ( 모든 서버들이 똑같습니다. )

클라이언트는 로딩을 제외한 모든 로직이 싱글 쓰레드로 돕니다.

그 이유는 게임을 진행하는 상황에서 멀티 쓰레드를 돌릴 경우

디버깅 자체가 불가능할 뿐만 아니라 어떤 에러가 발생할 지 아무도 모르기 때문입니다.

( 특히 오픈월드 게임은 더욱 그렇습니다. )

다만 Update( 객체들의 상태 변화 ) 와 Render( 화면 상에 그려지는 그림들 )를 분리하는 추세이긴 하지만

(Dx11 부터는 자체 적용)

그래도 멀티 쓰레드를 최소화 한다는 것은 변함이 없습니다.

서버는 항상 멀티 쓰레드를 이용 계속 쓰레드를 돌리면서 여러 유저들을 처리하는 역할을 맡습니다. 

이 과정에서 문제점이 발생하는데요. 바로 동기화입니다. 

간단하게 예를 들어보겠습니다. 게임을 제작할 때 중요한 것이 바로 유저들의 유입현황 수 입니다.

즉, 몇 명의 유저가 접속을 했는지

체킹하는 것이지죠. 밑에 예제 클래스를 만들어보겠습니다.

class User

{

int m_nUsercount; // 유저가 들어오면 카운트가 증가합니다.

 

void IncreaseUserCount()

{

m_nUsercount++;

}

}

 

이제 IncreaseUserCount() 함수를 통해 유저가 들어오면 카운트가 계속 증가합니다. 이 때 문제점이 발생하게 됩니다.

개발자가 원하는 대로 m_nUsercount가 제대로 증가해주면 좋겠지만 그렇지 않거든요. 

어떤 문제일까요? 바로 멀티 쓰레드를 사용하는 순간 비동기화가 진행되기 때문입니다. 

m_nUsercount는 유저가 들어올 때마다 증가하는건 맞지만 유저가 동시에 들어온다면 동일한 값을 가진 상태에서 

값이 증가해버리는거죠 즉, 제대로 올바른 데이터가 되지 않는다는 뜻입니다. 그 말은 바로 서버가 터지고 긴급 점검에 

들어가야 한다는 뜻입니다. (상상만 해도.... 벌벌...)

이 것을 방지하기 위해선 우리는 유저 카운트가 증가할 때마다 하나의 쓰레드만을 처리해야 합니다. 

게임 클라이언트처럼 순서대로 처리해야 하는거죠 

이 때를 위해 만들어진 것들이 Critical Section(크리티컬 섹션), Mutex(뮤텍스), Semaphore(세마포어) 입니다. 

이제 이 세 가지가 각각 무엇을 하는지 알아보겠습니다.

Critical Section 

- 크리티컬 섹션은 하나의 프로세스 내의 쓰레드 사이에서만 동기화가 가능합니다. 주로 가볍고 쉽게 사용할 수 있기 때문에 많이 사용됩니다. 

CRITICIAL_SECTION sc;

sc.Lock(); // Lock()을 하면 하나의 쓰레드만 입장 가능

do Somethig(); // 로직 처리

sc.UnLock(); // 여러 개의 쓰레드를 처리할 수 있게끔 돌려놓는다.

 

하나의 프로세스만을 관여하기 때문에 세 가지중에서 가장 빠릅니다. 굉장한 장점이죠 

하지만 그만큼 처리할 수 있는 예외가 얼마 없다는 뜻입니다. 바로 멀티 프로그램이죠

게임에서 말한다면 동일한 게임을 여러 개를 실행시키는 다중 클라이언트를 말합니다. 

(잘 모르시는 분들을 위해 더 쉽게 말하면 하나의 컴퓨터에서 여러 개의 LOL을 키는 것과 같습니다.)

하나의 프로세스에 대해서만 처리하기 때문에 이중처리가 불가능 한 것이죠 이럴 경우 데드락 현상이 발생하게 됩니다.

이것을 보안하기 위해 나온 것이 바로 Mutex입니다.

Mutex

- 커널 객체로 크리티컬 섹션보다 무겁습니다. 하지만 크리티컬 섹션과 다르게 Mutex는 여러 개의 프로세스의 스레드 사이에서 동기화가 가능합니다. 

즉, 여러 동일한 프로그램에서 접근하여 쓰레드가 돌아가도 처리가 가능합니다.

다중 실행을 막을 때 유용하게 사용됩니다. 

Comments