프로세스와 스레드는 아래와 같이 정의할 수 있다.
CS에 대한 기초지식이 없다면 무슨 뜻인지 이해가 잘 안될것인데
각각 작업의 단위, 실행 흐름의 단위 라는걸 이해해두고 조금더 알아보자.
프로세스 (Process)
- 운영체제로부터 자원을 할당받은 작업의 단위
스레드(Thread)
- 프로세스가 할당받은 자원을 이용하는 실행 흐름의 단위
프로그램과 프로세스
정적 프로그램 (Static Program)
전공자가 아니라면 '프로세스' 라는 워딩은 낯설것인데 '프로그램' 은 다소 친숙할거라 생각된다
프로그램이란 윈도우의 *.exe 파일이나 Mac의 *.dmg 파일과 같이 컴퓨터에서 실행 할 수 있는 파일을 통칭한다.
단, 아직 파일을 실행하지 않은 상태이기 때문에 정적 프로그램(Static Program)줄여서 프로그램 이라고 부른다.
이러한 프로그램을 개발하기위해 자바나 C, Python 과 같은 언어를 이용해 코드를 작성하는데
쉽게말해서 프로그램 == 코드 덩어리 라고 이해하면 된다.
프로세스 (Process)
프로그램이 그냥 코드 덩어리 라면,
프로세스는 프로그램을 실행 시켜 정적인 프로그램이 동적으로 변하여 프로그램이 돌아가고 있는 상태를 말한다.
즉 컴퓨터에서 작업 중인 프로그램을 의미한다.
위 개념은 절대 생소한 것이 아니다. ctrl + alt + del 단축키를 눌러 우리가 항상 보던 '작업' 관리자 를 열어보면 이 개념이 그대로 들어가있는걸 볼 수 있다.
모든 프로그램은 운영체제가 실행되기 위한 메모리 공간을 할당해 줘야 실행될 수 있다.
그래서 프로그램을 실행하는 순간 파일은 컴퓨터 메모리에 올라가게 되고, 운영체제로부터 시스템 자원(CPU)을 할당받아 프로그램 코드를 실행시켜 우리가 서비스를 이용할 수 있게 되는 것이다.
우리가 카카오톡을 이용한다면
카카오톡 프로그램(카카오톡을 구성하는 코드덩어리들)을 메모리에 올리고 CPU로부터 할당받아 이 코드덩어리를 실행시키면 프로세스가 되는것이다.
간단히 정리하자면
- 프로그램 : 코드 덩어리
- 프로세스 : 코드 덩어리를 실행한 것
위와 같이 다른 개념이지만 일반적으로 혼용해서 어휘를 사용하니 알잘딱으로 이해하면 된다.
스레드 (Thread)
프로세스의 한계
과거에는 프로그램을 실행할 때 프로세스 하나만을 사용해서 이용했지만
고도화됨에 따라 프로세스 작업 하나만으로 프로그램을 실행하기에 한계가 생겼다.
오늘날 파일을 다운받으며 유튜브를 보고 디코를 하고 카톡을 하고 노래를 듣고 이러한 멀티작업이 너무나 당연한 기능으로 생각하지만 과거에는 파일을 다운받으면 시작부터 끝까지 프로세스 하나를 사용하기 때문에 다운이 완료될때까지 하루종일 기다려야 했다.
그렇다고 동일한 프로그램을 여러 개의 프로세스로 만들게 되면, 그만큼 메로리를 차지하고 CPU 에서 할당받는 자원이 중복되게 된다.
스레드(Thread)는 이러한 프로세스의 한계를 해결하기 위해 탄생 하였다.
스레드의 개념
스레드란, 하나의 프로세스 내에서 동시에 진행되는 작업 갈래, 흐름의 단위 를 말한다.
크롬 브라우저를 실행하면 하나의 프로세스가 생성되는데
이 브라우저에서 유튜브도 보고 검색도 하고 노래도 듣고 쇼핑도 할것이다.
즉, 하나의 프로세스 안에서 여러가지 작업들 흐름이 동시에 진행되기 때문에 가능한 것인데, 이러한 일련의 작업 흐름들을 스레드라고 하며 여러개가 있다면 멀티(다중)스레드 라고 부른다.
일반적으로 하나의 프로그램은 하나 이상의 프로세스를 가지고 있고,
하나의 프로세스는 반드시 하나 이상의 스레드를 갖는다.
즉, 프로세스를 생성하면 기본적으로 하나의 main 스레드가 생성되며
스레드 2개, 3개 는 개발자가 성능을 고려하며 직접 프로그래밍하여 위치 시켜주어야 하는것이다.
프로세스 & 스레드의 메모리
프로그램이 실행되어 프로세스가 만들어지면 아래 4가지의 메모리 영역으로 구성되어 할당 받게 된다.
- 코드 영역(Code / Text) : 프로그래머가 작성한 프로그램 함수들의 코드가 CPU가 해석 가능한 기계어 형태로 저장되어 있다.
- 데이터 영역(Data) : 코드가 실행되면서 사용하는 전역 변수나 각종 데이터들이 모여있다.
- 스택 영역(Stack) : 지역 변수와 같은 호출한 함수가 종료되면 되돌아올 임시적인 자료를 저장하는 독립적인 공간,
Stack은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다.
만일 Stack 영역을 초과하면 우리가 알고있는 stack overflow 에러가 발생하게 된다. - 힙 영역(Heap) : 생성자, 인스턴스와 같은 동적으로 할당되는 데이터들을 위해 존재하는 공간
사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다.
위 그림을 보면 Stack 과 Heap 영역이 위아래로 화살표가 쳐 있는 것을 볼 수 있는데, 이는 코드 영역과 데이터 영역은 선언할 때 그 크기가 결정되는 정적 영역이지만,
스택 영역과 힙 영역은 프로세스가 실행되는 동안 크기가 늘어났다 줄어들기도 하는 동적 영역이기 때문에 이를 표현한 것이다.

스레드의 자원 공유
스레드는 프로세스가 할당 받은 자원을 이용하는 실행의 단위로서, 스레드끼리 프로세스의 자원을 공유하면서 프로세스 실행 흐름의 일부가 되기 때문에 동시 작업이 가능하다.
이때 스레드는 메모리영역(Code, Data, Heap, Stack)중 Stack만 할당받아 복사하고
Code, Data, Heap은 프로세스내의 다른 스레드들과 공유된다.
따라서 각각의 스레드는 별도의 stack을 가지고 있지만 heap 메모리는 고유하기 때문에 서로 다른 스레드에서 가져와 읽고 쓸 수 있게된다.
stack은 함수 호출 시 전달되는 인자, 되돌아갈 주소값, 함수 내에서 선언하는 변수 등을 저장하는 메모리 공간이기 때문에, 독립적인 스택을 가졌다는 것은 독립적인 함수 호출이 가능하다 라는 의미이다.
그리고 독립적인 함수 호출이 가능하다는 것은 독립적인 실행 흐름이 추가된다는 말이다.
즉, stack을 가짐으로써 스레드는 독립적인 실행 흐름을 가질 수 있게 되는 것이다.
반면, 프로세스는 기본적으로 프로세스 끼리 다른 프로세스의 메모리에 직접 접근할 수는 없다.
프로세스의 자원 공유
기본적으로 각 프로세스는 메모리에 별도의 주소 공간에서 실행되기 때문에, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수는 없다.
그렇다면 프로세스는 영원히 다른 프로세스 정보에 접근할 수 없을까?
현재 우리가 사용하는 대부분의 컴퓨터 프로그램을 보면 다른 프로그램에 있는 정보를 가져오는 경우를 흔하게 볼 수 있다.
이처럼 특별한 방법을 통해 프로세스가 다른 프로세스의 정보에 접근하는 것이 가능하다.
프로세스 간 정보를 공유하는 방법에는 다음과 같은 방법들이 있다.
- IPC (Inter-Process Communication) 사용
- LPC (Local inter-Process Communication) 사용
- 별도의 공유 메모리를 만들어서 정보를 주고받도록 설정
그러나 프로세스 자원 공유는 단순히 CPU 레지스터 교체뿐만이 아니라 RAM과 CPU 사이의 캐시 메모리까지 초기화되기 때문에 자원 부담이 크다는 단점이 있다. 그래서 다중 작업이 필요한 경우 스레드를 이용하는 것이 훨씬 효율적이라, 현대 컴퓨터의 운영체제에선 다중 프로세싱을 지원하고 있지만 다중 스레딩을 기본으로 하고 있다.
프로세스 & 스레드의 동시 실행 원리
우리가 음악을 들으면서 웹서핑을 하고, 메신저의 메시지를 확인할 수 있는 이유는 컴퓨터 내부적으로 프로세스와 스레드를 동시에 처리하는 멀티 태스킹(multi tasking) 기술 때문이다.
심플하게 CPU 프로세서가 프로그램들을 한꺼번에 동시에 돌리는 것으로 생각하겠지만, 내부적으로 복잡한 원리에 의해 처리가 된다. 그 원리를 알아보자.
멀티코어와 스레드
한번 컴퓨터 견적을 맞춰본 경험이 있으면 4코어8쓰레드 CPU 이런 단어를 본 적이 있을 것이다.
CPU 한 개는 여러개의 코어를 가질 수 있다. 코어는 말 그대로 CPU 코어 유닛이다.
즉, 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛이 4개가 있는 것이다.
4코어가 물리적 코어 갯수면, 8쓰레드는 논리적 코어 갯수를 말한다.
이 경우 물리적 코어 하나가 스레드 두 개 이상을 동시에 실행 가능하다는 의미가 된다.
즉, 운영체제가 8개의 작업을 동시에 처리할 수 있다는 뜻이다.
이를 하이퍼스레딩(Hyper-Threading)기술이라 말한다.
단, 여기서 CPU의 쓰레드는 우리가 배운 프로세스의 쓰레드와는 조금 다른 개념이다.
엄밀히 말하자면 CPU의 스레드는 하드웨어적 스레드이고 프로그램의 스레드는 소프트웨어적 스레드로 구분한다.
그런데 우리는 프로그램을 수십, 수백개를 켜 놓고 이용하는데 고작 8개의 논리적스레드로 어떻게 수백개의 프로세스를 처리할까?
이 원리를 알기 위해선 병렬성(Parallelism) 과 동시성(Concurrency) 이라는 개념을 알아야한다.
CPU의 작업 처리 방식
병렬성 (Parallelism)
병렬성은 직관적으로 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛인, 여러개의 코어에 맞춰 여러개의 프로세스,스레드를 돌려 병렬로 작업들을 동시 수행하는 것을 말한다.
동시성 (Concurrency)
동시성은 둘 이상의 작업이 동시에 실행되는 것을 의미한다. 이 '동시'라는 의미에서 병렬성과 동시성의 의미가 헷갈릴 수 있는데
Parallelism가 물리적으로 정말로 동시에 실행하는 것이라면, Concurrency는 동시에 실행하는 것처럼 보이게 하는것으로 이해하면 된다.
즉, 1개의 코어가 있고 4개의 작업이 있다고 가정하면, 아래 그림과 같이 프로세스들을 계속 번갈아가면서 조금씩 처리함으로써 마치 프로그램이 동시에 실행되는 것 처럼 보이는 것이다.
이때 작업들을 번갈아가면서 실행할때 작업들을 아주 잘게 나누어 아주 조금씩만 작업을 수행하고 다음 작업으로 넘어가는 식으로 동작한다.
사용자에게 동시에 처리하는것처럼 보이게 만들어, 더 빠른 반응성을 제공하기 위함인데
이렇게 진행중인 작업들을 A -> B -> C -> D 로 번갈아 바꾸는 것을 Context Switching 이라고 칭한다.
동시성이 필요한 이유
상식적으로 생각해보면 동시성(Concurrency)은 '동시에 돌아가는 것 처럼' 보이는 것이지, 실제로 동시에 돌아가는 것이 아니기때문에 최종 작업이 걸리는 시간은 거의 차이가 없을 것이다.
병렬성은 정말로 각 코어에 프로세스를 나눠 실행하는 거니까 듀얼 코어면 반 이상 줄어들텐데 말이다.
그렇다면 왜 이렇게 번거롭게 작업들을 스위칭하며 처리하는 것일까?
첫번째는 하드웨어적 한계 때문이다.
CPU는 발열 때문에 깡 클럭으로 성능을 올리기에는 한계에 봉착됬기 때문에 코어의 성능을 올리는 대신 코어를 여러개 탑재하여 쿼드코어, 옥타코어 CPU들을 출시하고 있지만,
아무리 코어를 때려박아도 수십, 수백개의 코어를 넣을순 없으니 수백개 이상의 프로세스를 돌리기 위해선 동시성이 필요한 것이다.
두번째는 보다 논리적인 효율 때문이다.
4코어 8스레드의 CPU 환경에서 현재 총 16개의 작업이 있다고 가정해보자.
그중 8개는 오래 걸리는 작업이고, 나머지 8개는 짧은 시간을 필요로 하면
논리적 코어는 8개이니 최대 8개까지 동시에 실행가능한데, 만약 최악의 경우 오래걸리는 8개작업이 먼저 처리되면
가벼운 나머지8개 작업은 짧은 시간이 걸리지만 선행된 8개의 작업이 다 끝날때 까지 기다려야 할 것이다.
이런 비효율적인 면을 극복하기 위해 작업을 아주 잘게 나눠 번갈아 가면서 처리하는 동시성 개념을 채택한 것이다.
따라서 적절히 병렬성과 동시성을 섞어 동시에 돌리면 된다.
'개발지식' 카테고리의 다른 글
플로우차트 (4) | 2025.03.01 |
---|---|
[Trouble Shooting] 라이브러리 버전호환의 중요성 (7) | 2024.12.30 |
코드 컨벤션 (Code Convention) (5) | 2024.11.07 |
판교사투리 (7) | 2024.11.07 |