본문 바로가기
Old/Network

Multi Threading

by onenewkong 2023. 7. 24.

 

"프로그램을 실행하면 프로세스가 생성됨. 프로세스 안에는 유일한 쓰레드가 있고 안에 프로그램 실행"

 

멀티쓰레드 프로그래밍 활용 용도)

  • 기기에 있는 CPU(Core) 모두 활용해야
  • Single Core 사용량이 100% 되지 않으면 멀티쓰레드 프로그래밍을 절대 하면 안됨
  • 오래 걸리는 1개와 빨리 끝나는 여러 같이 해야
  • 어떤 처리를 진행하는 동안 다른 짧은 처리해야

-> 비동기 IO 사용하여 프로세스가 I/O 완료를 기다리지 않게

  • 기기에 있는 CPU 모두 활용해야 (싱글 코어에서 처리 불가하고 CPU 클럭을 높여야 해결될 작업인 경우)                           
  • ex) FPS

 

쓰레드의 정체

: " 가지 일을 동시에 하라"라고 시켰을

  • 운영체제는 개의 작업을 각각 다른 Core에서 실행
  • 코어 개수 부족하면 컨텍스트 스위칭을 하면서 시분할 처리

데이터 레이스 현상: 멀티 쓰레드/프로세스 환경에서 일어나는 오류로, 여러 쓰레드/프로세스가 공유자원에 동시에 접근하려 , 일어나는 경쟁 상황을 의미

 

가령 쓰레드와 다른 쓰레드가 동시에 변수를 쓰려고 , 쓰레드는 변수를 쓰고 있는데 다른 쓰레드는 변수를 읽으려고 등의 상황에서 데이터 레이스가 발생함

 

해결법)

상호배제: 공유자원을 모든 쓰레드/프로세스가 원하는 언제든지 접근할 있도록 허용하지 않고, 쓰레드/프로세스의 접근을 제한적으로 허용하는 방식

-> 뮤텍스와 세마포어

 

데이터 레이스를 해결하기 위해 뮤텍스/세마포어를 여기저기 걸다보면 처리 시간이 느려짐

원인은 컨텍스트 스위칭이 일어나기 때문

CPU 동시에 개씩의 쓰레드/프로세스만 실행 가능, 멀티 쓰레드/멀티 프로세스란 단지 빠른 시간 동안 여러가지의 쓰레드/프로세스가 순차적으로 실행하면서, 동시에 실행되는 것처럼 보이는

 

이처럼 순차적으로 실행되는 과정에선 비용이 발생

뮤텍스의 관점에선 먼저 자원을 점유하고 있던 쓰레드/프로세스가 뮤텍스(세마포어) 언락(포스트)했을 , 대기하고 있던 다른 쓰레드/프로세스가 깨어나면서 컨텍스트 스위칭이 일어나고 이러한 컨텍스트 스위칭이 잦으면 오버헤드가 발생해 성능이 떨어짐

 

뮤텍스: 상호배제(mutual exclusion) 줄임말

사용 방법)

  1. X, Y 보호하는 뮤텍스 MX 만듦
  2. 쓰레드는 X, Y 건드리기 전에 MX "사용권을 얻겠다."라고 요청
  3. 쓰레드는 X, Y 액세스함
  4. 액세스가 끝나면 MX "사용권을 놓겠다."라고 요청함

 

교착상태

: 쓰레드가 서로를 기다리는 상황

게임 서버에서 교착 상태가 되면 발생하는 현상

  1. CPU 사용량이 현저히 낮거나 0%, 동시접속자 수와 상관없음
  2. 클라이언트가 서버를 이용할 없음, 예를 들어 로그인을 못하거나 뭔가 요청을 보냈는데 응답이 오지 않음

 

잠금 순서의 규칙

재귀 뮤텍스: 스레드가 뮤텍스를 여러 반복해서 잠그는 것을 원활하게 처리

재귀 잠금 - "교착 상태를 예방하려면 번째 잠근 순서를 지켜야 (거꾸로 가지 말아야 )"

 

병렬성과 시리얼 병목

  • 병렬성: 여러 CPU 스레드의 연산을 실행하여 동시 처리량을 올리는
  • 시리얼 병목: 병렬로 실행되게 프로그램을 만들었는데 정작 CPU 연산을 수행하는 현상

암달의 법칙(암달의 저주): CPU 개수가 많을수록 처리 효율성이 떨어지는 현상

암달의 저주를 줄이려면 시리얼 병목이 발생하는 구간을 최소로 줄여야

 

싱글 스레드 게임서버

  • 싱글 스레드 서버를 구동하는 경우 CPU 개수만큼 프로세스를 띄우는 것이 일반적
  1. 개수만큼 스레드나 프로세스가 있으면 스레드나 프로세스 컨텍스트 스위치 횟수 증가
  2. 따라서 같은 동시접속자를 처리하는 서버라고 하더라도 실제로 처리할 있는 동시접속자 수를 크게 떨어뜨림
  • 멀티스레드로 서버를 개발하는 경우
  1. 서버 프로세스를 많이 띄우기 곤란할 , 예를 들어 프로세스 로딩해야 하는 게임 정보( 데이터 ) 용량이 매우 (특히 MMO 게임 서버)
  2. 서버 대의 프로세스가 여러 CPU 연산량을 동원해야 만큼 많은 연산을
  3. 코루틴이나 비동기 I/O함수를 없고 디바이스 타임이 발생할
  4. 서버 인스턴스를 서버 기기당 하나만 두어야
  5. 서로 다른 방이 같은 메모리 공간을 액세스해야

 

쓰레드 풀링

  • 멀티쓰레드 모델의 게임 서버를 개발할 쓰레드는 만들고, 쓰레드는 무엇을 위해 일을 하게 만들면 좋을까?

 

이벤트: 잠자는 스레드를 깨우는 도구

  • C++11에서의 이벤트: future/promise, condition_variable 같은 것으로 구현
  • 이벤트는 작업이 없는 스레드를 재워 CPU 낭비를 막을 있으나(아니면 작업이 생길 때까지 루프를 실행하면서 기다려야 <= Busy Waiting)
  • 스레드의 실행 상태(running, blocked) 바꿔야 하므로 운영체제 레벨에서 구현이 되어야 하고, 운영체제 호출 오버헤드를 가짐
  • 따라서, 자주 실행되는 코드에서는 성능 문제로 사용하면 안됨

 

세마포어: 원하는 개수의 스레드가 자원을 액세스할 있게

  • 세마포어는 이벤트와 여러모로 비슷한데, 이벤트는 상태 값이 0 1 제한되지만, 세마포어는 0 이상의 아무 값이나 가질 있음

 

원자 조작

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

원자 조작은 하드웨어 기능이며, 대부분의 컴파일러는 원자 조작 기능을 있게

원자 조작은 32비트나 64비트의 변수 타입에 여러 스레드가 접근할 스레드씩만 처리됨을 보장

 

멀티스레드 프로그래밍 흔한 실수들

  1. 읽기와 쓰기 모두에 잠금하지 않기
  2. 잠금 순서 꼬임
  3. 너무 좁은 잠금 범위
  4. 디바이스 타임이 섞인 잠금
  5. 잠금의 전염성으로 발생한 실수
  6. 잠금된 뮤텍스나 임계 영역 삭제
  7. 일관성 규칙 깨기

 

'Old > Network' 카테고리의 다른 글

게임 네트워킹  (0) 2023.07.27
게임 서버와 클라이언트  (0) 2023.07.24
Socket Programming  (0) 2023.07.24
컴퓨터 네트워크  (0) 2023.07.24
REST / REST API  (0) 2023.02.11