프로그램(Program)
: 컴퓨터에서 실행되는 명령어 모음이 들어 있는 데이터 덩어리, 코드와 데이터로 구성되어 있다.
프로세스(Process)
: 프로그램 안에 들어 있는 명령어가 한줄씩 실행된다. 실행중인 프로그램
프로세스 메모리에는 코드와 데이터, 힙과 스택도 공존한다
스택에는 현재 실행 중인 함수들의 호출 기록과 사용 중인 로컬 변수들이 들어있다
멀티프로세싱(Multi-Processing)
: 프로세스가 여러개 실행되고 있는 것
스레드 (Thread)
: 명령어를 한 줄씩 실행하는 기본 단위
스레드와 프로세스의 차이
1. 스레드는 한 프로세스 안에 여러 개가 있다.
2. 한 프로세스 안에 있는 스레드는 프로세스 안에 있는 메모리 공간을 같이 사용할 수 있다.
3. 스레드마다 스택을 가진다. 즉, 각 스레드에서 실행되는 함수의 로컬 변수들이 스레드마다 있다는 의미이다
싱글스레드 프로그램
: 동시에 하나만 실행되는 프로그램
메인스레드(Main Thread)
: 기본으로 존재하는 스레드
멀티스레딩
: 여러 스레드가 동시에 여러 가지 일을 처리하게 되는 것
함수는 실행이 끝나면 자기 자신을 호출했던 호출자(caller) 함수를 실행했던 지점으로 되돌아가야 한다.
어디까지 실행하다 말았는지를 기억하고 있어야 한다.
이 정보가 저장되어 있는 곳을 호출 스택(Call Stack)이라고 한다.
호출 스택 안에는 각 함수 안에 선언된 지역 변수도 같이 들어있다.
각 스레드는 실행 지점이 서로 다를 수 밖에 없다.
스레드를 실행할 때는 그 스레드가 최초로 실행할 함수를 지정하는데, 이때 함수가 서로 다르기 때문이다.
스레드를 생성하려면 스레드 생성용 함수를 호출한다.
ex)
t1 = CreateThread(ThreadProc, 123);
인자엔 스레드가 최초로 실행할 함수와 그 함수가 받아들일 매개변수를 넣는다
ex)
int main()
{
t1 = CreateThread(ThreadProc, 123);
// ...
t1.Join();
return 0;
}
Join() 함수는 스레드 t1의 일이 모두 끝날 때까지 기다려라 이다.
스레드를 생성하라는 함수를 실행하면 스레드가 생성되며 지정한 함수를 실행한다
일단 시작한 스레드는 Runnable상태, 즉 프로그램의 명령어를 실행하는 상태로 바뀐다
다른 무언가를 기다릴 때는 Blocked 상태가 된다.
모든 일을 다 마치면 스레드를 Dead 상태가 되어 소멸한다
메인 스레드가 종료되었지만 다른 스레드를 그냥 남겨두면, 프로그램이 종료되지 않는 한 이상한 현상을 겪게 된다.
이를 좀비 프로세스라고도 한다
멀티스레드 프로그래밍을 해야 하는 대표적인 상황
1. 오래 걸리는 일 하나와 빨리 끝나는 일 여럿을 같이 해야 할때
2. 어떤 긴 처리를 진행하는 동안 다른 짧은 일을 처리해야 할때
3. 기기에 있는 CPU를 모두 활용해야 할 때
컨텍스트 스위칭(Context Switching)
: 각 스레드를 실행하다 말고 다른 스레드를 마저 실행하는 과정.
우선 실행 중이던 스레드의 상태를 어딘가에 저장하고, 과거에 실행하다가 만 다른 스레드 중에서 하나를 고른다. 고른 스레드의 상태를 복원하고, 실행하던 지점으로 강제 이동을 해야 한다.
경쟁 상태, 데이터 레이스(data race)
: 두 스레드가 데이터에 접근해서 그 데이터 상태를 예측할 수 없게 하는 것
원자성(Atomicity)
: 두 멤버 변수를 건드리는 동안에는 다른 스레드가 절대 건드리지 못하게 해야한다
일관성(Consistency)
: 두 변수는 항상 일관성 있는 상태를 유지할 수 있다
동기화(Synchronize)
: 원자성과 일관성을 유지하는 특수한 조치
뮤텍스(Mutex)
: 상호 배제(Mutual Exclusion)의 줄임말, 임계영역을 가진 스레드들의 러닝 타임이 서로 겹치지 않게 각각 단독으로 실행되게 한다
뮤텍스는 다른 말로 임계 영역(Critical Section)이라고도 한다.
뮤텍스에 대한 사용권을 얻는 과정은 잠근다(Lock)고도 표현한다
lock_guard
: 뮤텍스 잠금 상태를 로컬 변수로 저장하고, 그 변수가 사라질 때 자동으로 잠금 해제가 되게 하는 클래스
뮤텍스를 너무 잘게 나누지 않아야 한다
1. 프로그램 성능이 떨어진다. 뮤텍스를 액세스하는 과정 자체가 무겁기 때문이다
2. 프로그램이 매우 복잡해진다. 교착 상태 문제가 쉽게 발생한다
동시에(여러 CPU가 병렬로) 연산하면 유리한 부분은 잠금 단위를 나누고, 병렬로 하지 않아도 별로 성능에 영향을 주지 않는 부분들은 잠금 단위를 나누지 않는 것이 좋다
컨텐션(Contention)
: 두 스레드가 동시에 한 데이터를 액세스하려고 하는 상황
교착 상태(Deadlock)
: 두 스레드가 서로를 기다리는 상황
스레드 1은 스레드 2가 하던 일이 끝날 때까지 기다리는데, 스레드 2는 스레드 1이 하던 일이 끝날 때까지 기다리는 상황
교착 상태가 되면
1. CPU 사용량이 현저히 낮거나 0%이다. 동접수와 상관없다
2. 클라이언트가 서버를 이용할 수 없다
재귀 뮤텍스(Recursive Mutex)
: 한 스레드가 뮤텍스를 여러번 반복해서 잠그는 것을 원활하게 처리해준다
교착 상태를 예방하려면 첫번째 잠금 순서를 지켜야 한다
병렬성(Parallelism)
: 여러 CPU가 각 스레드의 연산을 실행하여 동시 처리량을 올리는 것
시리얼 병목(Serial Bottleneck)
: 병렬로 실행되게 프로그램을 만들었는데 정작 한 CPU만 연산을 수행하는 현상
암달의 법칙(Amdahl's Law)
: CPU 개수가 많을수록 총 처리 효율성이 떨어지는 현상
멀티스레드로 서버를 개발하는 경우
1. 서버 프로세스를 많이 띄우기 곤란할 때
ex) 프로세스당 로딩해야 하는 게임 정보의 용량이 매우 클 때
2. 서버 한 대의 프로세스가 여러 CPU의 연산량을 동원해야 할 만큼 많은 연산을 할 때
3. 서버 인스턴스를 서버 기기당 하나만 두어야 할 때
4. 코루틴이나 비동기 함수를 쓸 수 없고 디바이스 타임이 발생할 때
5. 서로 다른 방이 같은 메모리 공간을 액세스해야 할 때
이벤트
: 스레드 간 소통하며 일을 처리해야 할 때 유용하다
Reset
: 이벤트가 없음을 의미. 정수로 표현하면 0이다
Set
: 이벤트가 있음을 의미. 정수로 표현하면 1이다
자동 이벤트 모드에서는 이벤트 상태 값이 1이 되었을 때 이벤트를 기다리던 스레드가 있으면 그 스레드를 깨운다.
그리고 상태 값이 자동으로 0으로 바뀐다
수동 이벤트 모드에서는 이벤트 상태 값이 1이 되었을 때 이벤트를 기다리던 스레드가 깬다
0으로 바꾸는 것은 수동으로 해야한다
pulse 기능
: 이벤트에 딱 1회만 상태 값을 1로 바꾸고 즉시 다시 0으로 바꾸는 기능이다
세마포어(Semaphore)
: 원하는 개수의 스레드가 자원을 액세스할 수 있게 한다
뮤텍스나 임계 영역은 스레드 1개만 자원을 액세스할 수 있다
초기값은 최대 액세스 가능한 스레드의 개수이다
이벤트는 상태 값이 0과 1로 제한되지만, 세마포어는 0 이상의 아무 값이나 가질 수 있다
모니터(Monitor)
: 그 방법으로 활용하기 위해 구현된 기능 또는 모듈
어떤 공유 데이터에 대해 모니터를 지정해놓으면, 프로세스는 그 데이터를 접근하기 위해 모니터에 들어가야만 한다. 즉, 모니터 내부에 들어간 프로세스에게만 공유 데이터를 접근할 수 있는 기능을 제공하는 것이다. 또한 프로세스가 모니터에 들어가고자 할 때 다른 프로세스가 모니터 내부에 있다면 입장 큐에서 기다려야 한다.
틀리거나 다른 내용이 있다면 답글 달아주세요~
'프로그래밍 > 서버' 카테고리의 다른 글
소켓 프로그래밍 3 (0) | 2019.09.27 |
---|---|
소켓 프로그래밍 2 (0) | 2019.09.25 |
소켓 프로그래밍 1 (0) | 2019.09.23 |
컴퓨터 네트워크 2 (0) | 2019.09.20 |
컴퓨터 네트워크 1 (0) | 2019.09.19 |