#1 프로그램과 프로세스

게임 서버 프로그래밍/게임 서버 프로그래밍 교과서

2020. 2. 17. 15:59

1. 프로그램과 프로세스

1. 프로그램과 프로세스


1.프로그램과 프로세스
  • 프로세스

    프로그램안에 들어 있는 명령어가 한 줄씩 실행되면서

    프로그램이 무언가 활동하는 상태

  • 멀티프로세싱

    프로세스가 여러 개 실행되고 있는 것

2.스레드
  • 스레드

    프로세스처럼 명령어를 한줄씩 실행하는 기본단위

  • 프로세스와의 차이점

    한 프로세스에 여러 스레드가 존재한다.

    같은 프로세스 내의 스레드들은 메모리공간을 같이 사용할 수 있음

  • 싱글 스레드 프로그램

    동시에 하나만 사용되는 프로그램

  • 멀티스레딩

    여러 스레드가 동시에 여러가지 일을 처리하게 하는 것.

3.멀티스레드 프로그래밍을 해야할 떄
  • 오래 걸리는 일 하나와 빨리 끝나는 일 여럿을 같이 해야 할 때

    게임프로그램에서 로딩할 때

  • 어떤 긴 처리를 진행하는 동안 다른 짧은 일을 처리해야 할 때

    플레이어 정보를 읽거나 쓰려고 디스크를 엑세스 하는경우

    이 경우 CPU는 놀게된다.이 시간을 플레이어에게 분배하면 실행

    서능을 개선할 수 있다.

  • 기기에 있는 CPU를 모두 활용해야 할 때

4.스레드 정체
  • 컨텍스트 스위치

    • 프로세스 안에 있는 스레드들은 일정 시간마다 번갈아 가면서 실행을 한다.

      각 스레드를 실행하다 말고 다른 스레드를마저 실행하는 과정.

    • 컨텍스트 스위치는 적지않은 연산을 한다.

    • CPU 개수와 (Runnable)스레드 개수가 같거나 스레드 개수가 더 적으면

      컨텍스트 스위치가 발생할 이유가 없다.

  • 타임 슬라이스

    컨텍스트 스위치 실행에서 '사람 입장에서 쾌적할 수 있는 가급적

    긴시간 단위'

5.스레드를 다룰 때 주의 사항
  • 데이터 레이스

    두 스레드가 데이터에 접근해서 그 데이터 상태를 예측할 수 없게하는 것

    컨텍스트 스위치에 의해 결과예측이 불가능한 상태.

  • 트랜잭션

    데이터베이스의 상태를 변화시키고 논리적 기능을 수행하기위한

    작업단위 또는, 한꺼번에 모두 수행되어야 할 일련의 연산들이다

  • 원자성

    트랜잭션과 관련된 일은 모두 실행되거나 모두 실행되지 말아야 한다.

  • 일관성

    트랜잭션이 그 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터

    베이스 상태로 변환한다.

    • 일관성이란 데이터 타입이 갑자기 바뀌지 않는다는 의미(ex int -> string)

    원자성,일관성은 동기화를 통해 해결. 뮤텍스,잠금(Lock)의 방법 이용

  • 독립성

    트랜잭션을 수행하는 도중에 다른 연산작업이 끼어들지 못하게 한다.

    • 지속성

      성공적으로 트랜잭션이 수행되었다면, 그 결과는 완전히 반영이 되어야한다.

      Commit을 하면 현재 상태는 영원히 보장된다

6.임계 영역과 뮤텍스
  • 뮤텍스란 ?

    상호 배제(mutual exclusion)의 줄임말

    임계 영역(critical section)이라고도 한다.

  • lock_guard

    • C++에서 뮤텍스 잠금 상태를 로컬 변수로 저장하고, 그 변수가 사라질 때

    자동으로 잠금 해제가 되게 하는 클래스

    • 예외 발생시 unlock이 되지 않는 상태를 해결해 준다.
    • c#은 lock keyword로 해결한다.
  • 메모리 바운드 시간

    메모리에 접근하는 시간

7.교착 상태
  • 교착상태란?

    두 스레드가 서로를 기다리는 상황을 의미한다.

  • 교착상태에서 발생하는 현상

    • CPU 사용량이 현저히 낮거나 0%
    • 클라이언트가 서버를 이용할 수 없다.
8.잠금 순서의 규칙
  • 거꾸로만 잠그지 마라!

    잠금순서와 같은 순거로 잠금을 해제하라

    잠금순서를 정했다면 어떤 곳에서도 그 순서를 지켜야함.

  • 하나의 스레드 안에서 여러 원자들에 대해 따로 잠금을 해야하는 경우

    그 순서는 모든 스레드에서 동일하게 적용되어야 한다.

  • 재귀 뮤텍스란?

    한 스레드가 뮤텍스를 여러 번 반복해서 잠그는 것을 원할하게 처리해 줌

9.병렬성과 시리얼 병목
  • 병렬성

    여러 CPU가 각 스레드의 연산을 실행하여 동시 처리량을 올리는 것

  • 시리얼병목

    병렬로 실행되게 프로그램을 만들었는데 정작 한 CPU만 연산을 수행하는 현상

  • 암달의 저주

    CPU 개수가 많을수록 총 처리 효율성이 떨어지는 현상

    시리얼 병목이 발생하는 구간을 최소로 해야한다.

  • 디바이스타임

    기기에 있는 장치(네트워크 인터페이스,디스크 등)에 뭔가를 요청해서

    결과가 올 때까지 기다리는 시간

    CPU가 연산을 하지않으며 자기 떄문에 시간을 낭비한다.

    잠금하고나서 디바이스 타임을 갖지 말자. 시리얼 병목이 발생한다.

10.싱글스레드 게임 서버
  • 싱글스레드 서버를 구동하는 경우 CPU 개수만큼 프로세스를 띄우는 것

    일반적이다.

  • 싱글스레드로 서버를 만드는 경우, 디스크에서 플레이어 정보를 로딩할 때

    발생하는 디바이스 타임을 처리하는 과정에서 큰 시리얼 병목이 일어난다.

11.멀티스레드 게임 서버
  • 서버 프로세스를 많이 띄우기 곤란할 때.예를 들어 프로세스당 로딩해야 하는

    게임 정보의 용량이 매우 클 때(MMO, 맵 로딩)

  • 서로 다른 방이 같은 메모리 공간을 엑세스해야 할 때

    보통 방 단위로 잠금 범위를 설정하는 것이 적당하다.

12.스레드 풀링
  • 스레드 개수가 많을 때 발생하는 문제

    • 호출 스택에 따른 메모리의 낭비
    • 심각하게 발생하는 컨텍스트 스위치 현상
  • 스레드 풀링

    미리 쓰레드를 할당시켜 놓고 사용하는 기법. 이벤트가 발생하게 되면 노는 스레드를 찾아서 처리하고, 모든 스레드가 사용중이면 스레드가 사용 가능해 질 때까지 기다리게 된다.

13.이벤트

잠자는 스레드를 깨우는 도구. 스레드가 서로 소통해야하는 상황에 유리.

  • Reset

    이벤트가 없음. 정수 값으로 0으로 표현됨

  • Set

    이벤트가 있음. 정수 값으로 1으로 표현됨

  • 자동 이벤트 모드

    이벤트 신호를 가질 때, 이벤트를 기다리던 스레드가 있으면 그 스레드를 꺠운다. 그리고 상태 값이 자동으로 0으로 바뀐다.

    여러 스레드 중 한 스레드만 꺠어남.

  • 수동 이벤트 모드

    이벤트 신호를 가질 때, 이벤트를 기다리던 스레드가 있으면 그 스레드를 꺠운다. 상태값은 여전히 1이고, 이를 0으로 바꾸는 것은 수동으로 해결해야 한다.

    모든 스레드가 다 깨어남.

  • 맥박 기능

    • 이벤트 상태값을 0으로 바꾸는 것은 모든 스레드가 이벤트 대기에서 깨어난

    이후에만 할 수 있는데, 그 시점을 알 수 없음.

    • 딱 1회만 상태 값을 1로 바꾸고 즉시 다시 0으로 바꾸는 기능
14.셰마포어
  • 뮤텍스나 임계 영역은 오로지 스레드 1개만 자원을 엑세스 할 수 있다.

    셰마포어는 원하는 개수의 스레드가 자원을 엑세스 할 수 있게 한다.

  • 멀티 스레드는 컨텍스트 스위치에 의해 정상적인 순서를 벗어나는 행동을 할 수 있다.

    컨텍스트 스위치에 의해 스레드간 조건이 성립 안해 무한 루프가 도는 것을 방지하여

    이벤트의 상태가 1,0 뿐만 아니라 그 이상의 값도 가지게 할 수 있게 하는 것.

    (event는 돌발 상황에 의해 값이 2나 그 이상이 넘어가면 처리를 못함)

 

15.원자 조작

뮤텍스나 임계 영역 잠금 없이도 여러 스레드가 안전하게 접근할 수 있는것.

하드웨어 기능이다.

  • volatile

    이 변수는 여러 스레드에서 언제든지 엑세스 한다는 의미.

  • 원자성을 가진 값 더하기

    int r = AtomicAdd(&a,3)

    우리가 원하는 것은 a에 정확히 3이라는 값이 추가되는 것이다.

  • 원자성을 가진 값 맞 바꾸기

    int r = AtomicExchange(&a,10)

    a의 과거 값이 r로 채워지고, a에는 10이 들어간다

  • 원자성을 가진 조건부 값 맞바꾸기

    int r = AtomicCompareExchange(&a,10,100);

    리턴 값은 a를 바꾸기 전 값이다

16.팁
  • 잠금 하나로 모든 멤버 변수를 보호하자

    임계 영역이 많으면 유지 보수하기 어렵다.

  • 어떤 멤버 변수를 엑세스하는 동안 많은 연산량이나 디바이스 타임이 있을 때는

    잠금을 둘 이상으로 쪼갤 필요가 있다.

  • 잠금의 전염성으로 발생한 실수

    잠금으로 보호되는 리소스에서 얻어 온 값이나 포인터 주소 값 등이

    로컬 변수로 있는 경우에도 잠금 상태를 계속 유지하는 것.(unlock을 걸지 않는다)