프로세스(Process), 스레드(Thread)
시작하기에 앞서
우리는 자바 프로그래밍을 진행하다보면 멀티 쓰레딩을 마주치곤 한다.
나에게 프로세스가 무엇인가, 스레드가 무엇인가, 멀티 프로세싱과 멀티 쓰레딩의 차이가 무엇인가에 묻는다면 제대로 대답할 수 있을까에 대한 고민을 한 결과 그렇지 못했다.
그래서 이번 기회로 공부를 해보고자 이 포스트를 작성했다.
프로그램, 프로세스, 스레드
우리가 컴퓨터를 켜고 무슨 일을 하려고 한다면 프로그램을 켜서 작업을 진행한다.
프로그램은 정확히 무엇일까?
- 작업을 위해 실행할 수 있는 파일
- 저장 장치에 저장되어 있지만 메모리에 올라와 있지 않은 정적인 상태
그렇다면, 프로세스는 무엇일까?
- 메모리에 올라와 실행중인 프로그램
- 운영체제로부터 시스템 자원을 할당받는 작업의 단위
프로그램과 프로세스에 대해 요리로 비유해보자면, 프로그램은 요리를 만들기 위한 레시피에 해당된다.
프로세스는 레시피를 통해 만들어진 요리이다.
즉, 프로그램은 프로세스를 실행하기 위해 존재하는 파일이라고 생각하면 쉬울 것 같다.
그렇다면 프로그램이 프로세스가 되기 위해서는 어떤 과정을 거치는 것일까?
이 부분에 대해서 자세히 살펴보려면 OS(운영체제)에 대한 개념이 필요하다.
프로그램 -> 프로세스
프로그램을 실행되면 프로세스로 변경된다.
그 과정 자체를 살펴보자.
위에서 언급했던 것처럼 프로그램이 실행되어 메모리 위로 올라와 실행중인 상태가 되어야 한다.
그 과정은 다음과 같다.
- 생성 상태 : 프로그램을 메모리에 가져와 실행 준비가 완료된 상태, PCB가 생성된다.
- 준비 상태 : 프로세스가 메모리에 적재된 상태로, 실행을 기다리는 모든 프로세스가 자기 차례를 기다리는 상태
- 실행 상태 : 선택된 프로세스가 CPU를 사용하는 상태
- 대기 상태 : 실행 상태에 있는 프로세스가 입출력을 요청하면 입출력이 완료될 때 까지 기다리는 상태
- 완료 상태 : 프로세스가 종료된 상태, PCB가 삭제된다.
여기서 말하는 PCB는 Process Control Block)의 약자로 관리해야하는 정보를 포함하는 운영체제 커널의 자료구조다.
PCB가 저장 중인 정보 중 간단하게 몇몇을 살펴보면 다음과 같다.
- 프로세스 식별자(Process ID) : 프로세스를 구분하는 식별자
- 포인터(Pointer) : 부모 프로세스에 대한 포인터, 자식 프로세스에 대한 포인터, 프로세스가 위치한 메모리 주소에 대한 포인터 등
- 프로세스 상태(Process State) : 생성(Create), 준비(Ready), 실행(Running), 대기(Waiting), 완료(Terminated) 등의 상태가 존재한다.
- 프로그램 계수기(Program Counter) : 프로그램 계수기는 이 프로세스가 다음에 실행할 명령어의 주소를 가르킨다.
- 레지스터(Register) : Accumulator, CPU Register, General Register 등을 포함한다.
OS는 위에서 살펴봤던 것처럼 실행 상태에서 대기 상태로 갈 때 어디까지 실행되었고 다음 실행될 명령어는 무엇인지에 대한 정보 등을 알아내기 위해 만들어진 PCB를 바탕으로 파악하여 사용하게 된다.
이를 문맥 교환(Context Switching)이라고 하며 이 과정에서 사용하게 된다.
그렇다면 이렇게 실행되고 관리되어지는 프로세스는 어떤 것인지 자세하게 살펴보자.
프로세스
위에서 언급했던 것처럼 프로세스는 메모리에 올라와 실행되고 있는 프로그램이다.
운영체제로부터 시스템 자원을 할당받는 작업의 단위로 우리 컴퓨터 작업 관리자를 통해 쉽게 찾아 볼 수 있다.
여기 보이는 모든 것들이 프로세스이다.
구성
실제 프로세스는 메모리에 영역을 할당받아 다음과 같이 구성한다.
- Code : 실행 명령을 포함하는 코드들 (우리가 작성한 코드)
- Data : Static 변수 혹은 Global 변수
- Heap : 동적 메모리 영역
- Stack : 지역 변수, 인자 파라미터, 반환, 호출한 함수가 종료되면 되돌아올 메모리의 주소 등 일시적인 데이터
특징
- 위에서 살펴봤던 것처럼 각각 독립된 메모리 영역(Code, Data, Stack, Heap)을 할당받는다.
- 기본적으로 프로세스 당 최소 1개의 스레드(메인 스레드)를 가지고 있다.
- 각 프로세스는 별도의 주소 공간에서 실행되며, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다.
- 한 프로세스가 다른 프로세스의 자원에 접근하려면 프로세스 간의 통신(IPC)을 사용해야 한다.
스레드
스레드는 프로세스 내에서 실행되는 흐름의 단위로서, 프로세스 내에서 실제로 작업을 수행하는 주체를 의미한다.
스레드의 구조는 아래와 같다.
구성
- Code, Data, Heap 영역을 공유한다.
- Stack에 대해서는 따로 할당받는다.
특징
- 스레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들을 같은 프로세스 내에 스레드끼리 공유하면서 실행된다.
- 같은 프로세스 안에 있는 여러 스레드들은 같은 주소 공간이나 자원들을 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다.(IPC를 이용해야 한다.)
- 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드(sibling thread)도 그 변경 결과를 즉시 볼 수 있다.
멀티 프로세스, 멀티 스레드
멀티 프로세스
- 하나의 응용 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스들이 하나의 작업을 처리하도록 하는 것이다.
- 어느 하나의 프로세스에 문제가 생기더라도 해당 자식만 죽을 뿐 다른 곳에 영향을 끼치지 않는다.
- 문맥 교환을 위한 비용이 크다.
멀티 스레드
- 하나의 응용 프로그램을 여러 개의 스레드로 구성하여 각 스레드들이 하나의 작업을 처리하도록 하는 것이다.
- 프로세스를 생성하여 자원을 할당하는 콜이 줄어 자원을 효율적으로 관리할 수 있다.
- 문맥 교환을 위한 비용이 적고 Code, Data, Heap 영역을 공유하기에 통신 부하가 적다.
- 디버깅이 어렵고 동기화에 신경을 써야 한다.
- 한 스레드에 문제가 생기면 다른 곳에 영향을 끼친다.
예를 들어, 한 스터디 룸을 빌려 A팀이 스피커, 컴퓨터, 스크린, 노트북을 들고 갔다고 한다.
이용이 끝나 A팀이 나가고 B팀이 사용하는데 있어 멀티 프로세스는 스터디룸에서 공용으로 사용해도 되는 스피커, 컴퓨터, 스크린을 전부 다 들고가버려 B팀이 전부를 다 챙겨와야되는 것이라고 생각하면 된다.
반면, 멀티 스레드는 공용으로 사용해도 되는 스피커, 컴퓨터, 스크린을 두고가고 필요한 노트북만 챙겨오면 되는 것이다.
하지만 스피커, 컴퓨터, 스크린이 고장난다면 B팀도 사용하지 못하게 될 것임을 알 수 있다. (멀티 프로세스의 경우 무관)
자바 스레드
우리가 흔히 CPU에서 n코어 m스레드라고 많이들 광고하는 모습을 볼 수 있다.
위에서 살펴본 것처럼 스레드는 프로세스 내에서 실제로 작업을 수행하는 주체이자 작업의 단위이다.
만약 4코어 8쓰레드라면 8개밖에 안되는데 왜 자바에서 멀티 스레드를 사용하면 몇십개 몇백개로 설정해놓는 것일까?
이 부분을 살펴보기 위해 CPU의 구조에 대해 알아보자.
CPU는 한 코어당 하나의 작업을 처리한다.
CPU는 처음에 1코어 1스레드로 나오게 되었다.
우리가 보는 프로세스들은 1코어 1스레드가 작업을 우리가 볼 수 없는 새에 처리하면서 동시에 처리하는 것처럼 보이게 만든다.
이것이 바로 동시성이다.
하지만 CPU 성능의 고도화는 계속 필요로 해져왔고 CPU 성능에 따른 발열은 더이상 해결할 수 없게 되었다.
그래서 나온 것이 바로 멀티 코어이다.
한 코어당 성능을 낮추더라도 여러 개의 코어로 나누어 일을 처리하게 만든다면 속도가 올라갈 수 있을 것이다라는 생각에서 나오게 되었다.
이렇게 되면 1코어 1스레드가 1개의 작업을 순식간에 처리하는 반면 4코어 4스레드는 4개의 작업을 병행하면서 순식간에 처리한다.
이것이 바로 병렬성이다.
그런데, 한 코어의 성능을 한 스레드가 전부 뽑아먹지 못했다. 그래서 우리가 왼손 오른손을 쓰듯 한 코어로 두 가지의 작업을 처리할 수 있도록 만든 논리적 코어가 바로 여기서 말하는 스레드이다. (4코어 4스레드 -> 4코어 8스레드)
위에서 언급한 부분은 하드웨어 스레드로 실제로 일을 처리하는 스레드들이다.
우리가 자바에서 설정하는 스레드는 단순히 소프트웨어 스레드로 각각의 작업들을 병렬적으로 나누어 처리할 수 있도록 만들어 놓은 스레드에 불과하다.
이 스레드들을 많이 만들어둔다고 하더라도 실제 하드웨어 스레드에 의해서 계속적으로 실행되는 것이다.
예를 들면, 도서관에 책이 4세트 8권이 있다고 한다면 총 8명(하드웨어 스레드)이 빌릴 수 있는 것이다. 이 때, 독자 100명(소프트웨어 스레드)가 이 책을 빌리려고 한다면 반납이 된 후 빌려야 되는 구조이다. 이와 비슷하다고 생각하면 된다.
즉, 자바 스레드는 프로세스 처리를 위한 하나의 작업에 부여되는 스레드로 원하는 숫자만큼의 스레드를 할당할 수 있다.
하지만, 이를 실행하는 것은 하드웨어 스레드이기에 실행되지 않는 스레드는 기다리고 있을 것이고 너무 많은 수의 스레드를 한다면 메모리가 감당하지 못하는 사태가 발생할 수 있을 것이다.
참고
CPU가 지원하는 쓰레드랑 프로그래밍 상의 쓰레드랑 다른건가요? | KLDP