프로세스와 스레드

박병욱·2025년 7월 27일

Java

목록 보기
32/38
post-thumbnail

🎳 Multi-Tasking

어느 한 개발자가 유튜브 뮤직(프로그램 A)을 들으면서 인텔리제이(프로그램 B)로 신나게 개발하고 있다고 해보자. 만약 연산을 수행하는 CPU 코어가 1개만 있다고 가정했을 때…

이처럼 “프로그램의 실행” 이라는 의미는, 위의 그림을 보다시피 프로그램을 구성하고 있는 코드를 순서대로 CPU에서 연산(실행)하는 행위를 의미한다. CPU의 코어가 1개만 있다고 가정했으므로, 한 번에 하나의 프로그램 코드만 실행 가능하다. 이처럼 한 개의 프로그램의 코드가 모두 실행되어야만 다른 프로그램을 실행할 수 있다면… 유튜브 뮤직을 재생하면 다른 아무 작업도 하지 못한다면 화병에 걸릴게 뻔하다. 그래서 생각된 해결책은 단순하다. 하나의 CPU 코어로 여러 프로그램을 동시에 실행하면 된다.

 

그게 어떻게 가능한 것일까? 애니메이션을 생각하면 이해가 빠를 것이다. 한 장면 장면을 캡쳐해서 연속적으로 빠르게 교차해서 보여주면 사람은 그걸 움직이는 영상으로 인지한다. 그러니까 정확히 말하면, 물리적으로 동시에 실행하는 것이 아니고, 동시에 실행되는 것처럼 보이게 하는 것이다. (프로그램은 대략 10ms 단위로 돌아가며 실행)

이렇게 각각의 프로그램이 실행 시간을 분할해서 동시에 실행되는 것처럼 보이게 하는 기법을 시분할(Time Sharing) 기법이라 한다. 이런 식으로 하나의 컴퓨터 시스템이 여러 작업을 수행할 수 있는 능력, 그것을 멀티태스킹(Muti-Tasking)이라고 하는 것이다.


🎏 Multi-Processing

근데 만약 CPU 코어가 여러 개라면?

당연히 이제는 동시에 처리되는 것처럼 보이는 것이 아닌, 진짜 2개의 프로그램을 실행할 수 있을 것이다. CPU 코어들이 프로그램 A프로그램 B를 실행하다가 잠시 멈추고, 프로그램 C프로그램 A를 수행하는 식으로 2개보다도 더 많은 프로그램을 실행할 수 있다.

이처럼 멀티프로세싱(Multi-Processing)은 둘 이상의 프로세서(CPU 코어)를 사용해서 여러 작업을 동시에 처리하는 기술을 말한다.

정리하자면, 멀티프로세싱은 하드웨어 성능을 향상시키는 하드웨어의 관점이고, 멀티태스킹은 CPU 시간을 분할해서 각 작업에 할당하는 OS 소프트웨어의 관점이다.


🎭 프로세스 & 스레드

일단 아래 그림을 유심히 보자.

“OS 안에 프로세스가 있고, 프로세스 안에 스레드가 있다.”

프로그램이라는 것은 실행하기 전에는 단순히 코드가 들어있는 파일 덩어리에 불과하다. 프로그램을 실행했을 때, 비로소 프로세스라는 것이 만들어지는 것이다. 이렇게 운영체제 안에서 실행 중인 프로그램을 프로세스라고 한다. 자바의 클래스와 인스턴스 개념처럼, 프로그램이 클래스라고 한다면 프로세스는 인스턴스 같은 느낌이다.

위의 그림을 보다시피, 각 프로세스는 독립된 본인의 메모리 공간을 가지고 있고, 서로 간섭하지 않는다. 그 공간은 OS에서 별도의 작업 단위로 분리해서 관리되고, 프로세스가 서로의 메모리에 직접 접근할 수 없다.

 

<프로세스의 메모리 구성>

  • Code: 실행할 프로그램의 코드가 저장되는 부분
  • Data: 전역 변수 및 정적 변수가 저장되는 부분
  • Heap: 동적으로 할당되는 메모리 영역
  • Stack: 메서드 호출 시 생성되는 지역 변수와 반환 주소가 저장되는 영역

 

이런 프로세스 안에서 실행되는 작업의 단위를 스레드라고 한다. 프로세스 안에는 여러 개의 스레드가 존재할 수 있으며, 이 스레드들은 프로세스가 제공하는 동일한 메모리 공간을 공유한다. 다시 말해, 스레드는 본인이 속해 있는 프로세스의 코드, 데이터, 힙 영역에 접근 가능하다는 소리다. 그리고 자신만의 스택을 가지고 있다.

 

여기서 다시 짚고 넘어가자. “프로그램이 실행” 되는 것은 무슨 의미라고 했지? 프로그램을 메모리로 불러와서 프로세스를 만드는 것이라고 했다. 그럼 이 만들어진 프로세스로 뭘 어쩐다는거지? 여기서 좀 더 깊게 들어가자면, 프로그램이 실행된다는 것은 프로세스 안에 있는 코드가 한 줄씩 실행되는 것이다. 분명 누군가가, 뭔가 알 수 없는 힘이 그 코드를 한 줄씩 실행하고 있는 상황이다. 그 주체가 바로 스레드인 것이다.

 

정리하면, 프로세스는 실행 환경과 자원을 제공하는 “컨테이너 역할” 을 하는 것이고, 스레드는 CPU를 사용해서 “코드를 하나하나 실행” 하는 것이다.

 

🤔 멀티스레드는 왜 필요할까?

예를 들어, 인텔리제이 프로그램 안에서도 컴파일하는 작업, 자동 완성하는 작업, 테스트하는 작업 등 여러 작업이 있을 수 있다. 이처럼 여러 개의 스레드를 사용하면 작업들을 병렬로 나눠서 처리하는 것이 가능해진다.


🗃 스레드와 스케줄링

그래서 스레드는 어떤 순서와 규칙으로 실행되는데? 일단 OS는 내부에 스케줄링 큐라는 것을 가지고 있고, 각각의 스레드는 스케줄링 큐에서 대기한다.

<단일 코어 스케줄링>

위의 그림처럼 스레드 A1, 스레드 B1, 스레드 B2가 본인을 실행해 달라고 외치고 있고, CPU 코어는 스레드들에게 알겠으니 스케줄링 큐에서 대기하라고 하는 것이다. 약간 계산대 같은 느낌이다.

이렇게 OS는 스레드 A1을 큐에서 꺼내고 CPU를 통해 실행한 다음, 잠시 멈추고 다시 스케줄링 큐에 넣는다. 그리고 스레드 B1을 큐에서 꺼내 CPU를 통해 실행하는 이런 일련의 과정을 반복해서 수행한다. 지금은 손님은 여러 명인데 계산대는 하나라서 아주 불편하다.

 

<멀티 코어 스케줄링>

아까 말했다시피 CPU 코어가 2개 이상이면 한 번에 더 많은 스레드를 말 그대로 동시에 실행할 수 있다. 계산대가 몇 개 더 생기면 아주 빠르게 계산하고 빠져나갈 수 있을 것이다.

스레드 여러 개를 병렬로 실행하다가, 특정 스레드 수행을 잠시 멈추고, 다른 스레드를 CPU 코어를 통해 실행하는 과정을 반복한다.

 

🤔 참고 사항

CPU에 어떤 프로그램이 얼마만큼 실행될지는 운영체제가 결정하는데 이것을 스케줄링(Scheduling)이라 한다. 이때 단순히 시간으로만 작업을 분할하지는 않고, CPU를 최대한 활용할 수 있는 다양한 우선순위와 최적화 기법을 사용한다. 지금은 운영체제가 스케줄링을 수행하고, CPU를 최대한 사용하면서 작업이 골고루 수행될 수 있게 최적화한다는 정도로 이해하면 충분하다.


🔁 컨텍스트 스위칭(Context Switching)

멀티태스킹이 항상 효율적일까? 멀티태스킹의 상황을 그대로 사람에 빗대어 보면, 내가 회원가입 기능 구현하고 있는데, 친구가 갑자기 알림 기능을 수정해달라고 하면… 일단 기쁜(?) 마음으로 하던 것을 멈추고, 알림 기능의 코드를 잘 살펴볼 것이다. 무사히 수정을 마치고 다시 회원가입 기능을 구현하려고 했는데… 내가 어디까지 했지? 이 코드를 왜 썼더라… 하는 상황이 발생할 수도 있다.

마찬가지로, OS에서의 멀티태스킹도 마찬가지다. 예를 들어, 스레드 A를 멈추는 시점에 CPU에서 사용하던 이런 값들을 메모리에 저장해둬야 하고, 이후에 스레드 A를 다시 실행할 때 이 값들을 CPU에 다시 불러와야 한다. 이런 과정을 컨텍스트 스위칭이라고 하고, 이는 약간의 비용이 발생한다.

profile
도메인을 이해하는 백엔드 개발자(feat. OOP)

0개의 댓글