온라인 게임 프로그래밍에서 소켓은 파일 핸들 방식과는 다르다.

 

1. 클라이언트 개수만큼 소켓이 있어야 한다.

2. 파일 처리를 하는동안 스레드가 대기하는 일이 없어야 한다.

 

이러한 이유 때문에 소켓은 보통 비동기 입출력 상태로 다룬다. 소켓을 비동기 입출력으로 다루는 방식에는 크게 두 가지 방식이 있다.

1. 논블로킹 소켓

2. Overlapped I/O 방식

발전된 버전

3. epoll

4. I/O Completion Port (IOCP)

 

3.1 블로킹 소켓

스레드에서 네트워크 수신을 하는 함수를 호출하면, 수신할 수 있는 데이터가 생길 때까지 스레드는 waitable 상태 (블로킹) 가 된다.

 

3.3 블로킹과 소켓 버퍼

소켓 각각은 송신 버퍼와 수신 버퍼를 하나씩 가지고 있다.

 

TCP 송신

소켓.send(data) 함수는 블로킹 없이 바로 리턴된다. (송신 버퍼가 가득 찬 경우가 아니라면)

 

TCP 수신

수신 버퍼가 가득 차면 사용자는 소켓에서 데이터를 수신하는 함수를 호출하여 수신된 데이터를 꺼낼 수 있다.

수신 버퍼가 완전히 비어 있으면 블로킹이 일어난다.

 

3.5 수신 버퍼가 가득 차면 발생하는 현상

TCP 수신 함수인 recv()는 1바이트라도 수신할 수 있으면 즉시 리턴한다.

수신 함수가 수신 버퍼에서 데이터를 꺼내는 속도가 운영체제가 수신 버퍼의 데이터를 채우는 속도보다 느리면 수신 버퍼가 꽉 차게 된다.

ㄴ 보내는 쪽의 송신 함수 send()가 블로킹된다. TCP 연결이 끊어지는 것은 아니다.

ㄴ 실제 송신 속도는 데이터를 느리게 처리하는 쪽에 맞추어진다.

ㄴ TCP는 초당 보내는 데이터양이 수신자가 초당 수신할 수 있는 데이터양보다 많을 때, 송신자 측 운영체제가 알아서 초당 송신량을 줄인다. (흐름 제어)

 

UDP의 경우

받는 쪽에서의 수신 함수는 수신 버퍼에 데이터그램이 하나라도 있으면 즉시 리턴한다.

수신 버퍼가 가득 차도 송신 함수에서 블로킹이 발생하지 않는다. (데이터그램은 버려진다.)

ㄴ 수신버퍼의 처리 속도가 느리면 데이터그램 유실이 발생한다.

ㄴ UDP에는 흐름 제어 기능이 없기 때문에 UDP를 속도 제한 없이 마구 송신하면 주변의 네트워킹이 경쟁이 밀려 혼잡 현상이 발생할 수 있다.

 

3.6 논블록 소켓

네트워킹을 해야하는 대상이 여럿인 경우에는 네트워킹 개수만큼 스레드를 만들고 각 스레드는 각각의 네트워킹 대상과 데이터를 주고받도록 하면 된다.

ㄴ 네트워크 대상이 많지 않을 때는 문제가 없다.

ㄴ 스레드 개수가 수백 개, 수천 개인 경우에는 컨텍스트 스위치 문제 때문에 자원 낭비로 이어진다.

 

논블록 소켓

ㄴ 송신 함수, 수신 함수에서 블로킹이 발생하지 않는다. (즉시 리턴한다, 리턴 값은 성공, 혹은 would block 둘 중에 하나)

ㄴ 대부분 운영체제에서 소켓 함수가 블로킹되지 않게 하는 API를 추가로 제공한다.

ㄴ 한 스레드에서 여러 소켓을 한꺼번에 다룰 수 있다.

ㄴ 플랫폼마다 다른 오류 처리 코드를 반환하기 때문에 잘 처리해야 한다.

 

논블록 소켓으로 연결을 시도하는 경우

ㄴ would block을 리턴한 경우 would block이 끝났는지 확인하기 위한 방법을 사용해야 한다.

ㄴ 클라이언트 입장에서는 게임 루프마다 길이가 0바이트인 데이터를 송신함수로 보내 성공 값을 받으면 연결이 성공한 것으로 간주하면 된다.

ㄴ 서버 입장에서는 스핀 락 방식으로 구현할 수 없다. poll 함수를 활용해야 한다. (CPU를 최대한 아껴야 하기 떄문에)

 

Poll 함수 (select 함수)

ㄴ 소켓 리스트와 블록 시간을 입력값으로 받는 함수

ㄴ 리스트 내 소켓중 하나라도 I/O 처리를 할 수 있게 되면 바로 리턴, 아니라면 지정된 타임아웃 시간 이후 리턴

 

논블록 accept

ㄴ 블로킹 모드인 경우 리스닝 소켓에 대해 accept()를 호출하면 accept()는 블로킹이 걸린다. 그리고 TCP 연결이 들어오면 리턴을 하는데, 이때 accept()의 리턴 값은 새 TCP 연결에 대한 소켓 핸들이다.

ㄴ 논블록 소켓을 사용한 I/O 함수는 연결이 안되었으면 블로킹 대신 would block 오류 코드를 주기 때문에 반복해서 재시도 호출을 해야 한다. Poll 함수를 사용하여 I/O 가능 이벤트가 감지될 때 accept()를 호출하도록 하여 CPU 사용량 폭주 문제를 해결해야 한다.

 

3.7 Overlapped I/O 혹은 비동기 I/O

논블록 소켓의 장점

ㄴ 스레드 블로킹이 없으므로 중도 취소 같은 통제가 가능하다.

ㄴ 스레드 개수가 1개여도 여러 소켓을 다룰 수 있다. (호출 스택 메모리 비용, 컨텍스트 스위치 비용이 작다)

 

논블록 소켓의 단점

ㄴ 소켓 I/O 함수가 리턴한 코드가 would block인 경우 재시도 호출 낭비가 발생한다.

ㄴ 소켓 I/O 함수를 호출할 때 입력하는 데이터 블록에 대한 복사 연산이 발생한다.

ㄴ connect() 함수는 재시도 호출을 하지 않지만, send() 함수나 receive() 함수는 재시도 호출해야 하는 API가 일관되지 않는다는 문제가 있다.

 

재시도 호출 낭비의 예시

UDP의 send()는 송신 버퍼가 1바이트라도 비어 있으면 I/O 가능인데 UDP는 데이터의 일부만 보낼 수 없으므로 넣을 수 있는 데이터 크기만큼의 공간이 비어있지 않다면 would block이 발생한다. I/O 가능인데 would block이므로 CPU로 낭비로 이어진다.

 

소켓 함수 내부의 데이터 복사 부하의 예시

ㄴ CPU 안에 있는 캐시 메모리에 내용이 복사되어 있으면 데이터 액세스는 매우 빠르지만, 캐시에 없는 데이터를 액세스할 때는 RAM을 액세스하는데 이것은 느리다.

 

Overlapped I/O는 이 문제들을 해결할 수 있다.

ㄴ Overlapped I/O 함수는 즉시 리턴되지만, 운영체제로 해당 I/O 실행이 별도로 동시간대에 진행되는 상태이기 때문에 I/O 함수가 비동기로 하는 일이 완료될 때까지 소켓 API에 인자로 넘긴 데이터 블록을 제거하거나 내용을 변경해서는 안된다.

ㄴ I/O 함수의 인자로 보내는 Overlapped status 구조체 또한 중간에 내용을 건드려서는 안된다.

 

Overlapped I/O 전용 송신 함수를 호출하면 운영체제는 송신할 데이터가 저장되어 있는 메모리 블록 자체를 송신 버퍼로 사용한다. 수신할 때는 수신 데이터 블록 자체를 수신 버퍼로 사용한다.

 

논블록 방식 대비 Overlapped I/O의 장단점

장점

소켓 I/O 함수 호출 후 would block 값인 경우 재시도 호출 낭비가 없다.

소켓 I/O 함수를 호출할 때 입력하는 데이터 블록에 대한 복사 연산을 없앨 수 있다.

send, receive, connect, accept 함수를 한 번 호출하면 이에 대한 완료 신호는 딱 한 번만 오기 때문에 프로그밍 결과물이 깔끔하다.

I/O completion port와 조합하면 최고 성능의 서버를 개발할 수 있다.

윈도우 플랫폼에서만 사용할 수 있다.

 

 

단점

완료되기 전까지 Overlapped status 객체나 데이터 블록을 중간에 훼손하지 말아야 한다.

accept, connect 함수 계열의 초기화가 복잡하다.

 

논블록 소켓은 리액터 패턴, Overlapped I/O는 프로액터 패턴이다.

 

3.8 epoll (논블록 방식)

epoll은 안드로이드와 리눅스에서만 사용 가능하다.

IOS, MacOs, FreeBSD에서는 epoll과 매우 유사한 kqueue를 사용하면 된다.

 

epoll에 논블록으로 설정된 소켓을 

 

select를 쓰면 모든 소켓에서 루프를 돌지만 epoll을 사용하면 I/O 가능인 상태의 소켓에서만 루프를 돌면 된다. 

소켓의 송신 버퍼가 빈 공간이 없는 순간을 유지하는 순간은 짧기 때문에 대부분의 소켓은 I/O 가능 상태이다. (비효율적이다) 

이 문제를 해결하기 위해 레벨 트리거 대신 에지 트리거를 써야 한다.

 

레벨 트리거 : I/O 가능이면 epoll에서 항상 꺼내어진다.

에지 트리거 : I/O 가능이 아니었다가 가능으로 변하는 순간에만 epoll에서 꺼내어진다.

 

에지 트리거를 쓸 때 주의해야 할 점

1. I/O 호출을 한 번만 하지 말고 would block이 발생할 때까지 반복해야 한다.

2. 소켓은 논블록으로 미리 설정되어 있어야 한다.

 

3.9 IOCP

epoll은 논블록 소켓을 대량으로 갖고 있을 때 효율적으로 처리해 주는 API이다.

IOCP는 Overlapped I/O를 다루는 운영체제에서 사용하는 API이다.

 

IOCP는 소켓의 Overlapped I/O가 완료되면 이를 감지해서 사용자에게 알려 주는 역할을 한다. 

ㄴ epoll과 비슷하지만 epoll은 I/O 가능인 것을 알려 주지만, IOCP는 I/O 완료인 것을 알려 준다.

 

epoll과 비교했을 때 사용법에 복잡한 기능이 몇 가지 있는데, 그중 하나가 Accept 처리이다.

1. IOCP에 listen socket L을 추가했다면, L에서 TCP 연결을 받을 경우 이에 대한 완료 신호가 IOCP에 추가됩니다.

2. 단 사전에 이미 AcceptEx로 Overlapped I/O를 건 상태여야 한다.

3. IOCP로 L에 대한 이벤트를 얻어 왔지만, 앞서 Overlapped accept처럼 SO_UPDATE_ACCEPT_CONTEXT와 관련된 처리를 해 주어야 새 TCP 소켓 핸들을 얻어 올 수 있다.

 

IOCP는 epoll에서 할 수 없는 성능상 유리한 기능이 있다. (스레드 풀링을 활용하여 여러 가지 일을 스레드 몇 개가 골고루 분담해서 처리할 수 있다)

'읽은 책 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글

2. 컴퓨터 네트워크  (2) 2022.09.09
1. 멀티 스레딩  (0) 2022.09.08

 

2.3 컴퓨터 네트워크 데이터

OSI 모델의 2계층에서는 데이터 단위를 프레임이라 하고, 3계층에서는 패킷이라고 한다.

프로그래머는 이것들 대신 스트림과 메세지라는 것을 주로 다룬다.

 

2.3.1 스트림 형식

스트림 : 두 단말기를 연결한 후 그 연결을 끊기 전까지 한쪽에서 다른 한쪽으로 연결된 데이터 흐름

 

컴퓨터 네트워크에서 스트림의 개념은 파일의 스트림과 다르다.

ㄴ 데이터가 여러 부분으로 

ㄴ 스트림 송신 횟수와 수신 횟수가 불일치할 수 있다.

 

데이터가 여러 부분으로 나뉘어질 수 있다면 사용자가 어떻게 받을지 정의를 해주어야 한다.

ㄴ 데이터의 크기를 헤더로 붙이는 방식

ㄴ 구분자를 이용하는 방식

 

2.3.2 메세지 형식

보낸 데이터의 수와 받는 데이터의 수가 같다. (각 데이터가 정확히 구별된다)

 

 

운영체제에 내장된 네트워크 모듈인 네트워크 스택에서 프로그램이 보내고 받는 스트림과 메세지를 관리해주기 때문에

IP패킷의 크기가 제한적이더라도 스트림이나 메세지에서는 크기 제한이 없다.

 

프로그램이 매우 긴 스트림을 송신할 때 운영체제는 이를 IP 패킷의 크기 제한에 맞추어 여러 조각을 낸다 (단편화)

각 조각은 IP 패킷 하나하나가 되어 받는 쪽에 송신한다.

받는 쪽에서의 복원 과정도 운영체제 안에서 진행된다.

 

2.4 컴퓨터 네트워크 식별자

IPv4는 4바이트

IPv6는 16바이트

포트는 2바이트

 

2.5 컴퓨터 네트워크의 품질과 특성

2.5.1 네트워크의 품질을 저해하는 것들

 

1. 스위치나 라우터가 자신이 처리할 수 있는 한계를 넘는 데이터를 수신할 경우

ㄴ 자기가 처리할 수 있는 것 이상을 그냥 버리기

ㄴ 아직 처리하지 못한 것들을 메모리에 누적하기

 

2. 데이터가 오가는 선로에 문제가 있는 경우

ㄴ 회선 신호가 약하거나 잡음이 섞이면 패킷 유실이 발생할 수 있다.

 

2.5.2 전송 속도와 전송 지연 시간

전송 속도 : 두 기기 간에 초당 전송될 수 있는 최대 데이터 양, 보통 bps로 표현 (= 두 단말기 사이에 있는 네트워크 기기 중 최소 스루풋에 의해 결정)

레이턴시 : 두 기기 간에 최소량의 데이터를 전송할 때 걸리는 시간 (= 두 단말기 사이에 있는 네트워크 기기의 레이턴시 총합)

 

전송 속도에 영향을 주는 것

ㄴ 선로의 종류와 품질

ㄴ 두 기기의 소프트웨어와 하드웨어 종류

 

레이턴시에 영향을 주는 것

ㄴ 선로의 종류와 품질

ㄴ 송신자-수신자 사이의 라우터 처리 속도

 

송신자와 수신자 사이에는 스위치, 라우터, 방화벽 같은 네트워크 기기들이 있고 각각의 기기들은 수신한 데이터를 처리 가능한 데이터로 변환하고 그것에 대한 연산 처리를 하기 때문에 시간을 차지한다.

 

2.5.3 네트워크 품질 기준 세 가지

전송 속도, 패킷 유실률, 레이턴시

 

2.5.4 무선 네트워크의 품질

다른 기기들이 동시에 신호를 보내면 신호가 변조되기 때문에 다른 전파가 감지되는지 확인 후 데이터를 보내기 때문에 (CSMA) 레이턴시가 길게 나온다.

 

2.6 컴퓨터 네트워크에서 데이터 보내기와 받기

2.6.1 UDP 네트워킹 (User Datagram Protocol)

받는 쪽의 프로그램과 보내는 쪽의 프로그램이 각자 데이터 통신에 사용할 포트를 할당하고 있어야 한다.

UDP를 이용하여 데이터그램을 보낼 수 있는데, 데이터그램은 64KB 이하의 이진 테이터로, 스트림이 아닌 메세지 성질을 가진다.

ㄴ 데이터그램 내용이 훼손되지는 않지만 패킷 유실이 발생할 수 있다. (데이터그램을 못 받거나 같은 데이터그램을 두 번 이상 받을 가능성이 있다)

ㄴ 받는 쪽에서 데이터그램 유실이나 순서 뒤바뀜 혹은 중복 수신 현상이 발생해도 괜찮은 경우에만 UDP를 사용하는 것이 좋다. (예를 들면 동영상이나 음성 데이터, 네트워크 게임에서는 캐릭터의 이동 정보)

 

UDP로 데이터를 주고받으려면 소켓을 생성하고 포트를 바인드 한뒤 송신, 수신 작업을 진행하면 된다.

 

UDP의 특징

수신용 소켓과 송신용 소켓을 따로 만들지 않아도 된다.

다대다 통신이 가능하다. (하나의 소켓이 여러개의 소켓으로 송수신 할 수 있다)

 

2.6.2 TCP 네트워킹

데이터를 주고받기 전에 연결을 해줘야 한다.

TCP는 메세지 형태가 아니라 스트림 형태로 데이터들을 전달한다.

 

UDP는 IP 패킷 유실이 발생하면 UDP 데이터그램도 드롭되지만 TCP는 똑같은 상황에서도 흐름 제어 기능이 있기 때문에 데이터가 상대방에게 정확하게 전송된다.

 

TCP에서 보낼 스트림 데이터는 세그먼트라는(IP 패킷안에 넣을 수 있는 크기) 단위로 쪼개져 수신자에게 전송된다.

수신자가 IP 패킷을 받으면 ack를 회신하는데 보낸 쪽에서는 일정 시간 안에 ack이 회신되지 않으면 응답이 올때까지 다시 세그먼트를 보낸다.

 

TCP 소켓 사용법

보내는 쪽

1. TCP 소켓 생성

2. 포트 바인딩

3. 연결 (받는 쪽 ip:port) (블로킹)

4. 송신 (이미 연결 되어있으므로 ip:port가 아닌 데이터만 사용)

5. 필요한 만큼 송신했다면 연결을 끊는다. (close)

 

받는 쪽

1. TCP 소켓 생성

2. listen (포트)

3. accept() (연결 될때 까지 블로킹, 새로운 소켓 생성)

4. 반복문내에서 recv()로 데이터를 받는다. 연결 상태를 유지하다가 0바이트 크기의 데이터를 받으면 반복문을 탈출하고 연결을 끊는다. (close)

ㄴ UDP에서는 데이터그램을 메세지 형식으로 주고받는다. 0바이트 메세지도 허용한다. TCP는 크기 0의 데이터를 받으면 연결이 종료되었음을 의미한다.

 

TCP 소켓도 UDP 소켓과 마찬가지로 소켓 하나로 송수신을 모두 할 수 있다.

 

2.7 패킷 유실 시 UDP와 TCP에서 현상

UDP는 1만 바이트의 데이터그램을 UDP로 전송하면 운영체제는 이를 1300바이트 정도의 크기 8개로 나누어 IP 패킷 8개로 전송되는데 이들 중 하나라도 유실되면 수신자는 8개 모두 못 받는다. (1만바이트짜리 데이터그램이 유실된다)

 

TCP에서는 유실되는 패킷의 재전송 시간만큼 지연 시간이 발생한다.

 

UDP의 레이턴시 = 네트워크 기기의 레이턴시

TCP의 레이턴시 = 네트워크 기기의 레이턴시 + 패킷 유실률 * 재전송 대기 시간

 

네트워크 게임 개발에서 거의 모든 메세지 종류에는 TCP를 사용하고 특정 상황에서만 UDP를 사용하지만 실제 통신량의 대부분은 UDP가 차지한다.

 

2.8 주로 사용하는 메세지 형식

1. 텍스트 형식

ㄴ HTTP나 JSON 같은 표준화된 형식을 씀

 

2. 바이너리 형식

ㄴ 각 바이트 혹은 비트 필드는 특정한 의미를 가진다.

ㄴ 텍스트 형식에 비해 구문 분석기가 필요 없어서 처리 성능이 더 낫고, 통신량도 적다

 

메세지 안에 내용을 담는 것뿐만 아니라 그 메세지 내용이 어떤 것을 담고 있는지의 정보를 같이 포함시키는 형식

 

ㄴ 메타 데이터를 포함시키면 데이터 통신량이 많아지고, 해커에게도 더 쉽게 노출된다는 단점이 있지만 하위 호환성 측면에서 유리하다.

ㄴ 받는쪽과 보내는쪽의 버전이 달라도 호환성 문제를 해결할 수 있다.

 

2.9 네트워크 주소 변환

네트워크 주소 변환 (NAT : network address translation)

ㄴ 다른 단말기로 전송되던 패킷의 송신자 주소나 수신자 주소가 다른 것으로 변환되는 과정 

ㄴ 인터넷 공유기는 NAT 라우터이다.

 

공유기 안에 있는 기기가 공유기 밖에 있는 기기에 패킷을 보내려고 하면 패킷을 통과하여 수신지에 전달

공유기는 내부 주소와 외부 주소의 매핑 정보를 생성해서 가지고 있는다.

 

2.11 더 읽을 거리

TCP 흐름 제어 (ARQ)

ㄴ slicing window (윈도우 크기만큼의 패킷을 응답받지 않고 보내기)

ㄴ 송신측의 처리 속도가 수신측의 처리 속도보다 빨라서 수신측의 저장용량을 초과한 데이터가 손실되는 것을 방지하기 위함

 

TCP 혼잡 제어

ㄴ AIMD (additive increase, multiplicative decrease), slow start, fast retransmit, fast recovery

 

네트워크 게임의 성능 최적화를 위해 UDP 위에 흐름 제어를 별도로 구현하는 경우도 있다.

 

송신자와 수신자 간 오가는 메세지는 제 3자가 도청하거나 변조할 수 있기 때문에 메세지를 암호화해야 한다.

'읽은 책 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글

3. 소켓 프로그래밍  (0) 2022.09.10
1. 멀티 스레딩  (0) 2022.09.08

1.4 스레드 정체

CPU 개수와 Runnable 스레드 개수가 같거나 Runnable 스레드 개수가 더 적으면 컨텍스트 스위치가 발생할 이유가 없다. 하지만 Runnable 스레드 개수가 더 많으면 컨텍스트 스위치가 어느 CPU 안에서는 반드시 발생한다. (비둘기집 원리)

 

멀티스레딩을 사용하여 소수 구하기

1 ~ 150000 사이의 소수들을 찾는 프로그램

멀티 스레드를 활용하지 않았을 때
4개의 스레드를 활용할 때

 

1.8 잠금 순서의 규칙

 

여러 뮤텍스를 사용할 때 교착 상태를 예방하려면 각 뮤텍스의 잠금 순서를 먼저 그래프로 그려 두고, 잠금을 할 때 잠금 순서 그래프를 보면서 거꾸로 잠근 것이 없는지 체크해야 한다.

 

재귀 뮤텍스

ㄴ 한 스레드가 같은 뮤텍스를 여러 번 반복해서 잠궈도 된다. 물론 잠금 횟수만큼 풀어야 한다.(재귀 잠금이 가능하다.)

ㄴ 재귀 잠금은 잠금 순서 그래프에 상관 없이 교착 상태를 일으키지 않는다. (첫 잠금에서만 순서를 지키면 된다)

 

1.9 병렬성과 시리얼 병목

여러 CPU가 각 스레드의 연산을 싫애하여 동시 처리량을 올리는 것을 병렬성이라고 한다. 병렬로 실행되게 프로그램을 만들었는데 정작 한 CPU만 연산을 수행하는 현상을 시리얼 병목이라고 한다.

 

 

시리얼 병목이 있을 때 CPU 개수가 많을수록 총 처리 효율성이 떨어지는 현상을 암달의 법칙(Amdahl's Law)라고 한다.

 

Visual Studio의 Concurrency Visualizer도구

ㄴ 멀티 스레드 프로그램이 병렬적으로 여러가지 일들을 동시에 잘 수행하는지 분석하여 시각화해서 보여주는 도구

 

 

https://marketplace.visualstudio.com/itemsitemName=Diagnostics.DiagnosticsConcurrencyVisualizer2019#overview

 

4개의 스레드를 사용한 프로그램을 분석한 경우

1.10 싱글 스레드 게임 서버

 

1.11 멀티 스레드 게임 서버

 

1.12 스레드 풀링

처리할 이벤트를 큐에 쌓아두고 비어 있는 스레드에 이벤트를 처리하도록 배분하는 방식이다.

스레드 개수를 클라이언트 개수만큼 두지 않고 스레드 풀링을 사용한다.

 

CPU 코어가 8개 있는 8개의 스레드를 실행시키는 상황에서 스레드가 CPU 연산만 하는 것이 아니라 데이터베이스나 파일 등에 액세스하는 스레드여서 CPU 사용율이 25%이고 디바이스 타임이 75%인 상황이라면 스레드 풀의스레드의 갯수를 4배 늘려 CPU를 놀게 하지 않게 해야 한다.

 

CPU 연산이 주 역할인 서버의 스레드 풀의 스레드 개수는 서버의 CPU 코어 개수와 동일하게 잡아도 충분하다.

 

1.13 이벤트

잠자는 스레드를 깨우는 도구로써 사용된다.

event는 내부적으로 0과 1의 상태 값을 가진다. (이벤트가 없으면 0 있으면 1)

 

윈도우 API에 이벤트 관련 함수가 있다.

https://docs.microsoft.com/ko-kr/windows/win32/sync/using-event-objects

 

이벤트 개체 사용(동기화) - Win32 apps

애플리케이션은 여러 상황에서 이벤트 개체를 사용하여 대기 스레드에 이벤트 발생을 알릴 수 있습니다.

docs.microsoft.com

 

자동 이벤트

ㄴ 이벤트 상태 값을 0으로 갖고 있다가 신호를 받으면(SetEvent) 상태 값이 1로 바뀌는데 이때 이벤트를 기다리던(잠들어 있던) 스레드 (Wait)가 있다면 그 스레드를 깨우고 자동으로 0의 상태 값으로 바뀐다.

ㄴ 두개 이상의 스레드를 이벤트를 기다리고 있었다면 여러 스레드중 하나만 깨어난다.

 

수동 이벤트

ㄴ 이벤트가 신호를 받으면 대기하고 있던 모든 스레드가 깨어난다.

ㄴ SetEvent()를 한 스레드에서 호출하면 대기하던 스레드에서 SetEvent(0)을 호출하여 수동으로 이벤트 상태 값을 되돌려줘야 한다. 하지만 이 방법을 사용하면 대기하던 스레드중 일부만 깨어날 가능성이 있다.

ㄴ PulseEvent()를 사용하여 이벤트에 신호를 주면 모든 스레드를 깨우면서 상태값을 자동으로 0으로 되돌려 준다.

 

1.14 세마포어

뮤텍스는 1개의 스레드만 자원을 액세스할 수 있게 하지만 세마포어는 원하는 개수의 스레드가 자원을 액세스할 수 있게 한다.

 

Wait() ~ Release() (mutex의 lock unlock같은 느낌)을 사용한다.

 

윈도우 API에 세마포어 관련 함수가 있다.

https://docs.microsoft.com/ko-kr/windows/win32/sync/using-semaphore-objects

 

세마포 개체 사용 - Win32 apps

다음 예제에서는 세마포 개체를 사용하여 특정 작업을 수행할 수 있는 스레드 수를 제한합니다.

docs.microsoft.com

 

스레드 간에 공유되는 큐가 있고 한 스레드는 큐에 항목을 넣고 다른 한 스레드는 큐에서 항목을 꺼내 쓰는 디자인에 활용하기 좋다.

 

초기값이 0인 세마포어를 만든 뒤, 항목을 넣는 스레드에서는 세마포어.Release()를 하면서 큐에 푸시하고 꺼내는 스레드에서는 Wait()를 하여 세마포어 값이 1 이상인 경우에만 큐에서 하나씩 요소를 꺼낸다.

 

1.15 원자 조작

원자 조작은 뮤텍스나 임계 영역 잠금 없이도 여러 스레드가 안전하게 변수에 접근할 수 있게 해준다.

32비트나 64비트 변수 타입에 여러 스레드가 접근할 때 한 스레드씩만 처리됨을 보장한다.

mutex보다 속도가 더 빠르다.

 

https://lemonyun.tistory.com/103

 

C++ memory order, atomic객체

atomic #include std::atomic counter(0); 여러 쓰레드에서 atomic 객체 자원을 수정할 때 원자적 연산(CPU가 명령어 한개로 처리하는 명령)을 사용하면 mutex를 사용하지 않아도 정상적인 결과가 나온다. memory..

lemonyun.tistory.com

 

'읽은 책 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글

3. 소켓 프로그래밍  (0) 2022.09.10
2. 컴퓨터 네트워크  (2) 2022.09.09

+ Recent posts