관리 메뉴

나구리의 개발공부기록

프로세스와 스레드 소개, 멀티태스킹과 멀티프로세싱, 프로세스와 스레드, 스레드와 스케줄링, 컨텍스트 스위칭 본문

인프런 - 실전 자바 로드맵/실전 자바 - 고급 1편, 멀티스레드와 동시성

프로세스와 스레드 소개, 멀티태스킹과 멀티프로세싱, 프로세스와 스레드, 스레드와 스케줄링, 컨텍스트 스위칭

소소한나구리 2025. 2. 9. 17:56
728x90

출처 : 인프런 - 김영한의 실전 자바 - 고급1편 (유료) / 김영한님  
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용


1. 멀티태스킹과 멀티프로세싱

1) 멀티태스킹과 멀티 프로세싱

(1) 단일 프로그램 실행

  • 연산을 처리할 수 있는 CPU코어가 1개만 있고 2개 이상의 프로그램을 동시에 실행한다고 가정
  • 프로그램의 실행은 프로그램을 구성하는 코드를 순서대로 CPU에서 연산(실행)하는 일이며 CPU코어는 하나로 가정했으므로 한 번에 하나의 프로그램 코드만 실행할 수 있음
  • 하나의 프로그램안에 있는 코드를 모두 실행한 후에야 다른 프로그램의 코드를 실행할 수 있다면 위의 그림처럼 프로그램A가 모두 끝나고 프로그램 B가 실행됨
  • 예를 들면 음악 프로그램이 모두 끝난 뒤에야 워드 프로그램을 실행할 수 있다면 컴퓨터 사용자는 매우 답답할 것이며 실제로 초장기의 컴퓨터를 한 번에 하나의 프로그램만 실행했었음
  • 이를 해결하기 위해 하나의 CPU 코어로 여러 프로그램을 동시에 실행하는 '멀티 태스킹' 기술이 등장함

(2) 멀티 태스킹

  • 현대의 CPU는 초당 수십억 번 이상의 연산을 수행하는데, 만약 CPU가 매우 빠르게 두 프로그램의 코드를 번갈아서 수행한다면 사람이 느낄 때에는 두 프로그램이 동시에 실행되는 것 처럼 느껴질 수 있음
  • 이 방식은 CPU 코어가 프로그램 A의 코드를 0.01초 정도 수행하다가 잠시 멈추고 프로그램B의 코드를 0.01초 정도 수행하고 다시 프로그램A의 이전에 실행중인 코드로 돌아가서 0.01초 정도 코드를 수행하는 방식으로 반복 동작함
  • 이렇게 각 프로그램의 실행 시간을 분할해서 마치 동시에 실행되는 것 처럼 하는 기법을 시분할(Time Sharing, 시간 공유)기법이라 함
  • 이 방식을 사용하면 CPU 코어가 하나만 있어도 여러 프로그램이 동시에 실행되는 것 처럼 느낄 수 있어 하나의 컴퓨터 시스템이 동시에 여러 작업을 수행하는 능력을 멀티태스킹(Multitasking)이라 함

** 참고

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

(3) 멀티 프로세싱

  • 여기서는 프로그램은 A, B, C 3가지이고, CPU는 2개가 있다고 가정
  • CPU 코어가 2개이므로 실제 물리적으로 동시에 2개의 프로그램을 처리할 수 있음
  • CPU 코어들이 프로그램A와 프로그램B를 실행하다가 잠시 멈추고, 프로그램C와 프로그램A를 수행하는 식으로 코어가 2개여도 2개보다 더 많은 프로그램을 실행할 수 있음
  • 멀티프로세싱(Multiprocessing)은 컴퓨터 시스템에서 둘 이상의 프로세서(CPU 코어)를 사용하여 여러 작업을 동시에 처리하는 기술을 의미하며 멀티프로세싱 시스템은 하나의 CPU코어만을 사용하는 시스템보다 동시에 더 많은 작업을 처리할 수 있음

** 참고

  • CPU에는 실제 연산을 처리할 수 있는 코어가 있는데 과거에는 하나의 CPU 안에 보통 하나의 코어만 있었음
  • 그래서 CPU와 코어를 따로 분리해서 이야기하지 않았으나 현대의 컴퓨터는 최소 하나의 CPU안에 2개 이상의 코어가 들어있음

(4) 멀티프로세싱 vs 멀티태스팅

  • 멀티프로세싱은 하드웨어 장비의 관점이고 멀티태스킹은 운영체제 소프트웨어의 관점임
  • 지금 그림에서의 예시는 여러 CPU코어를 사용하기 때문에 멀티프로세싱인 동시에 각각의 단일 CPU 코어에 여러 작업을 분할해서 수행하기 때문에 멀티태스킹임
  • 멀티프로세싱
    • 여러 CPU(여러 CPU코어)를 사용하여 동시에 여러 작업을 수행하는 것을 의미
    • 하드웨어 기반으로 성능을 향상시킴
    • ex)다중 코어 프로세서를 사용하는 현대 컴퓨터 시스템
  • 멀티태스킹
    • 단일 CPU(단일 CPU코어)가 여러 작업을 동시에 수행하는 것처럼 보이게 하는 것을 의미
    • 소프트웨어 기반으로 CPU 시간을 분할하여 각 작업에 할당
    • ex) 현대 운영 체제에서 여러 애플리케이션이 동시에 실행되는 환경

2. 프로세스와 스레드

1) 프로세스

(1) 프로세스

  • 프로그램은 실제 실행하기 전까지는 단순한 파일에 불과하며 프로그램을 실행하면 프로세스가 만들어지고 프로그램이 실행됨
  • 이렇게 운영체제 안에서 실행중인 프로그램을 프로세스라고 하며 프로세스는 실제 실행 중인 프로그램의 인스턴스
  • 자바 언어로 비유를 하면 클래스는 프로그램, 인스턴스는 프로세스로 볼 수 있음
  • 각 프로세스는 독립적인 메모리 공간을 갖고 있으며 운영체제에서 별도의 작업 단위로 분리해서 관리됨
  • 각 프로세스는 별도의 메모리 공간을 갖고 있기 때문에 서로 간섭하지 않고 프로세스가 서로의 메모리에 직접 접근할 수 없음
  • 프로세스는 이렇게 서로 완전히 격리되어 관리되기 때문에 하나의 프로세스가 충돌해도 다른 프로세스에는 영향을 미치지 않아서 특정 프로세스에 심각한 문제가 발생하면 해당 프로세스만 종료되고 다른 프로세스에 영향을 주지 않음
  • 예를 들면 게임을 실행하는 도중에 음악이 종료되어도 게임은 계속 돌아감

(2) 프로세스의 메모리 구성

  • 코드 섹션: 실행할 프로그램의 코드가 저장되는 부분
  • 데이터 섹션: 전역 변수 및 정적 변수가 저장되는 부분(그림에서 기타에 포함)
  • 힙 (Heap): 동적으로 할당되는 메모리 영역
  • 스택 (Stack): 메서드(함수) 호출 시 생성되는 지역 변수와 반환 주소가 저장되는 영역(스레드에 포함)
  • 자바의 객체지향을 공부할때 배웠던 영역과 상당히 유사한데 자바도 프로세스이기 때문에 이것을 추상화해서 이해하기 쉽게 제공할 뿐이기 때문임

2) 스레드 (Thread)

(1) 스레드

  • 프로세스는 하나 이상의 스레드를 반드시 포함함
  • 프로세스 내에서 실행되는 작업의 단위로 한 프로세스 내에서 여러 스레드가 존재할 수 있으며 이들은 프로세스가 제공하는 동일한 메모리 공간을 공유함
  • 스레드는 프로세스보다 단순하므로 생성 및 관리가 단순하고 가벼움

(2) 메모리 구성

  • 공유 메모리: 같은 프로세스의 코드 섹션, 데이터 섹션, 힙(메모리)은 프로세스 안의 모든 스레드가 공유함
  • 개별 스택: 각 스레드는 자신의 스택을 갖고 있음

(3) 프로그램이 실행된다는 의미

  • 프로그램을 실행하면 운영체제는 먼저 디스크에 있는 파일 덩어리인 프로그램을 메모리로 불러오면서 프로세스를 만듦
  • 프로그램이 실행된다는 것은 프로세스 안에 있는 코드가 한 줄씩 실행되는 것이며 자바에서는 main()부터 시작해서 하나씩 순서대로 내려가면서 실행됨
  • 어떤 무언가가 코드를 하나씩 순서대로 실행하기 때문에 프로그램이 작동하고 계산도하고 출력도 할 수 있음
  • 마치 실(thread)같은 것이 코드를 위에서 아래로 하나씩 꿰면서 내려가는 것 처럼 보이는 프로세스의 코드를 실행하는 흐름을 스레드(thread)라고 함
  • 스레드는 프로세스 내에서 실행되는 작업의 단위이며 한 프로세스 내에 하나 이상의 프로세스가 존재할 수 있으며 스레드는 프로세스가 제공하는 동일한 메모리 공간을 공유함
  • 단일 스레드: 한 프로세스 내에 하나의 스레드만 존재
  • 멀티 스레드: 한 프로세스 내에 여러 스레드가 존재
  • 하나의 프로세스 안에는 최소 하나의 스레드가 존재해야 프로그램이 실행 될 수 있음
  • 지금까지 강의에서 작성한 자바 코드들은 모두 한 프로세스 내에서 하나의 스레드만 사용하는 단일 스레드였음
  • 정리하면 프로세스는 실행 환경과 자원을 제공하는 컨테이너 역할을 하고 스레드는 CPU를 사용해서 코드를 하나하나 실행함

(4) 멀티스레드가 필요한 이유

  • 하나의 프로그램도 그 안에서 동시에 여러 작업이 필요함
  • 워드 프로그램으로 문서를 편집하면서 문서가 자동으로 저장되기도 하고 맞춤법 검사도 함께 수행됨
  • 유튜브는 영상을 보는 동안 댓글도 달 수 있음
  • 운영체제 관점에서 보면 아래와 같이 구분할 수 있음
  • 워드 프로그램 - 프로세스 A
    • 스레드1: 문서 편집
    • 스레드2: 자동 저장
    • 스레드3: 맞춤법 검사
  • 유튜브 - 프로세스 B
    • 스레드1: 영상 재생
    • 스레드2: 댓글

3. 스레드와 스케줄링

1) 스레드와 스케줄링

(1) 구조 설명

  • CPU 코어는 1개이고 프로세스는 2개이며 프로세스 A는 스레드1개 프로세스 B는 스레드 2개가 있음
  • 1. 프로세스A에 있는 스레드 A1을 실행
  • 2. 프로세스A에 있는 스레드 A1의 실행을 잠시 멈추고 프로세스 B에 있는 스레드 B1을 실행
  • 3. 프로세스B에 있는 스레드 B1의 실행을 잠시 멈추고 같은 프로세스의 스레드 B2를 실행
  • 4. 이후에 프로세스 A에 있는 스레드 A1을 실행
  • 위 순서를 반복하는 예제로 멀티태스킹의 스케줄링 과정을 분석

(2) 단일 코어 스케줄링

  • 운영체제는 내부에 스케줄링 큐를 가지고 있고 각각의 스레드는 스케줄링 큐에서 대기함
  • 1. 스레드 A1, 스레드 B1, 스레드 B2가 스케줄링 큐에 대기
  • 2. 운영체제는 스레드 A1을 큐에서 꺼내 CPU를 통해 실행함, 스레드 A1이 프로그램의 코드를 수행하며 CPU를 통한 연산도 일어남
  • 3. 운영체제는 스레드 A1을 잠시 멈추고 스케줄링 큐에 다시 넣음
  • 4. 운영체제는 스레드 B1을 큐에서 꺼내고 CPU를 통해 실행함
  • 5. 이런 과정을 반복하여 스레드 A1, 스레드 B1, 스레드 B2를 실행함

(3) 멀티 코어 스케줄링

  • CPU 코어가 2개 이상이면 한 번에 더 많은 스레드를 물리적으로 진짜 동시에 실행할 수 있음
  • 1. 스레드 A1, 스레드 B1, 스레드 B2가 스케줄링 큐에 대기
  • 2. 스레드 A1, 스레드 B1을 병렬로 실행하고 스레드 B2는 스케줄링 큐에 대기
  • 3. 스레드 A1의 수행을 잠시 멈추고 스레드 A1을 스케줄링 큐에 다시 넣음
  • 4. 스케줄링 큐에 대기 중인 스레드 B1을 CPU 코어1에서 실행하고 조금 있다가 CPU 코어2에서 실행중인 스레드 B2도 수행을 멈추고 스레드 스케줄링 큐에 있는 다른 스레드가 실행됨
  • 5. 이런 과정을 반복해서 수행함

2) 프로세스, 스레드와 스케줄링 - 정리

(1) 멀티태스킹과 스케줄링

  • 멀티태스킹이란 동시에 여러 작업을 수행하는 것임
  • 이를 위해 운영체제는 스케줄링이라는 기법을 사용하며 스케줄링은 CPU 시간을 여러 작업에 나누어 배분하는 방법임

(2) 프로세스와 스레드

  • 프로세스: 실행 중인 프로그램의 인스턴스이며 각 프로세스는 독립적인 메모리 공간을 가지며 운영체제에서 독립된 실행 단위로 실행됨
  • 스레드: 프로세스 내에서 실행되는 작은 단위로 여러 스레드는 하나의 프로세스 내에서 자원을 공유하며 프로세스의 코드, 데이터, 시스템 자원등을 공유함, 실제로 CPU에 의해 실행되는 작업의 단위는 스레드임

(3) 프로세스의 역할

  • 프로세스는 실행 환경을 제공하며 메모리 공간, 파일 핸들, 시스템 자원(네트워크 연결) 등이 포함되는 컨테이너 역할을 함
  • 프로세스 자체는 운영체제의 스케줄러에 의해 직접 실행되지 않으며 프로세스 내의 스레드가 실행됨
  • 1개의 프로세스 안에 하나의 스레드만 실행되는 경우도 있고 1개의 프로세스 안에 여러 스레드가 실행되는 경우도 있음

4. 컨텍스트 스위칭

1) 컨텍스트 스위칭

(1) 사람의 멀티태스킹

  • 프로그램A를 개발하고 있는 중에 갑자기 프로그램B를 수정해달라고 요청이 와서 프로그램A의 개발을 멈추고 프로그램B를 수정한다고 가정했을 때, 프로그램B의 수정을 마치고 다시 프로그램A를 개발하기 위해 돌아가려면 프로그램A의 어디를 개발하고 있었는지 해당 코드의 위치를 찾아야 하고 개발할 때 변수선언한 변수들에 어떤 값들이 들어가는지 머리속의 기억들을 다시 불러와야 함
  • 만약 프로그램A의 개발이 다 끝나고 나서 프로그램B를 수정한다면 전체 시간으로 보면 더 효율적일 수 있음

(2) 컴퓨터의 멀티태스킹

  • CPU코어가 하나, 스레드A, 스레드B가 있을 때 멀티태스킹으로 프로그램을 실행한다고 가정
  • 먼저 스레드 A를 실행하고 멀티태스킹을 해야하기 때문에 스레드A를 잠시 멈추고 스레드B를 실행함
  • 이후에 다시 멀티태스킹으로 스레드A로 돌아가야하는데 그냥 돌아갈 수는 없고 스레드A의 코드가 어디까지 수행되었는지 위치를 찾고, 계산하던 변수들의 값을 CPU에 다시 불러들여야 함
  • 즉, 스레드A를 멈추는 시점에 CPU에서 사용하던 여러 값들을 메모리에 저장해둔 이후에 스레드A를 다시 실행할 때 이 값들을 CPU에 불러와야하는데 이런 과정을 컨텍스트 스위칭(context switching)이라 함
  • 컨텍스트는 현재 작업하는 문맥을 뜻하며 현재 작업하는 문맥이 변하기 때문에 컨텍스트 스위칭이라고 함
  • 컨텍스트 스위칭 과정에서 이전에 실행 중인 값을 메모리에 잠깐 저장하고 이후에 다시 실행하는 시점에 저장한 값을 CPU에 다시 불러와야하기 때문에 컨텍스트 스위칭 과정에는 약간의 비용이 발생함

(3) 멀티스레드는 대부분 효율적이지만 무조건 효율적이지는 않음

  • 예를 들어서 스레드1과 스레드2가 각각 1 ~ 5000, 5001 ~ 10000까지 더하고 마지막에 스레드1의 결과를 스레드 2의 결과에 더해서 1 ~ 10,000까지 더해야한다고 가정할 때 문제는 둘로 나뉠 수 있음
  • CPU 코어가 2개일 때
    • 모든 CPU를 사용하므로 연산을 2개 빠르게 처리할 수 있어 스레드1, 스레드2로 나누어 멀티스레드로 병렬 처리하는게 효율적임
  • CPU 코어가 1개
    • CPU코어가 1개 있는데 스레드를 2개로 만들어서 연산하면 중간중간 컨텍스트 스위칭 비용이 발생함
    • 운영체제 스케줄링 방식에 따라서 다르겠지만 스레드1을 멈추고 다시 실행할 때 어디까지 연산했는지 알아야하고 그 값을 CPU에 다시 불러와야하기 때문에 이렇게 반복할 때마다 연산 시간 + 컨텍스트 스위칭 시간이 들게됨
    • 이런 경우 단일 스레드로 1 ~ 10,000까지 더하는 것이 컨텍스트 스위칭 비용 없이 연산 시간만 사용하기 때문에 더 효율적임
    • 실제로는 컨텍스트 스위칭에 걸리는 시간은 아주 짧아서 대부분의 상황에서는 멀티스레드가 더 효율적이지만 스레드를 잘못사용하거나 스레드가 매우 많다면 컨텍스트 스위칭 비용이 커질 수 있음

** 참고

  • 최신 CPU는 초당 수 십억 단위를 계산하기 때문에 실제로는 계산에 더 큰 숫자를 사용해야 컨텍스트 스위칭이 발생함

2) 실무 이야기

(1) CPU - 4개, 스레드 2개

  • 스레드의 숫자가 너무 적으면 모든 CPU를 100% 다 활용할 수 없음
  • 스레드가 몇 개 없으므로 컨텍스트 스위칭 비용이 줄어듦

(2) CPU - 4개, 스레드 100개

  • 스레드의 숫자가 너무 많으면 CPU를 100% 다 활용할 수 있지만 컨텍스트 스위칭 비용이 늘어남

(3) CPU - 4개, 스레드 4개

  • 스레드의 숫자를 CPU의 숫자에 맞춘다면 CPU를 100% 활용할 수 있고 컨텍스트 스위칭 비용도 자주 발생하지 않기 때문에 최적의 상태가 됨
  • 이상적으로는 CPU 코어 수 + 1개 정도로 스레드를 맞추면 특정 스레드가 잠시 대기할 때 남은 스레드를 활용할 수 있음

(4) CPU 바운드 작업 vs I/O 바운드 작업

  • CPU-바운드 작업(CPU-bound tasks)
    • CPU의 연산 능력을 많이 요구하는 작업을 의미함
    • 이러한 작업은 주로 계산, 데이터 처리, 알고리즘 실행 등 CPU의 처리 속도가 작업 완료 시간을 결정하는 경우임
    • ex) 복잡한 수학 연산, 데이터 분석, 비디오 인코딩, 과학적 시뮬레이션 등
  • I/O-바운드 작업(I/O-bound tasks)
    • 디스크, 네트워크, 파일 시스템 등과 같은 입출력(I/O) 작업을 많이 요구하는 작업을 의미함
    • 이러한 작업은 I/O 작업이 완료될 때까지 대기 시간이 많이 발생하며, CPU는 상대적으로 유휴(대기) 상태에 있는 경우가 많음
    • 즉, 스레드가 CPU를 사용하지 않고 I/O 작업이 완료될 때 까지 대기함
    • ex) 데이터베이스 쿼리 처리, 파일 읽기/쓰기, 네트워크 통신, 사용자 입력 처리 등

(5) 웹 애플리케이션 서버

  • 분야마다 다르지만 실무에서는 CPU-바운드 작업 보다는 I/O-바운드 작업이 많음
  • 백엔드 개발자의 경우 주로 웹 애플리케이션 서버를 개발할 때 CPU의 연산이 필요한 작업보다는 대부분 사용자의 입력을 기다리거나, 데이터베이스를 호출하고 그 결과를 기다리는 등의 I/O-바운드 작업이 많음
  • 일반적인 자바 웹 애플리케이션 서버의 경우 사용자의 요청 하나를 처리하는데 1개의 스레드가 필요하므로 사용자 4명이 동시에 요청하면 4개의 스레드가 작동해야 4명의 사용자의 요청을 동시에 처리할 수 있음
  • 사용자의 요청을 하나 처리하는데 스레드는 CPU를 1% 사용하고 대부분 I/O-바운드 작업이 많다고 가정하면 CPU 코어가 4개 있다고 해서 스레드 숫자도 CPU 코어에 맞춰서 4개로 설정하면 안됨
  • 동시에 4명의 사용자 요청만 처리할 수 있으므로 CPU는 단순하게 계산해도 4% 정도만 사용하게 되어 CPU가 놀고 있는 사태가 벌어질 수 있음
  • 사용자의 요청 하나를 처리하는데 CPU를 1%만 사용한다면 단순하게 생각한다는 가정하에는 100개를 만들 수 있고 이렇게 하면 100명의 사용자 요청을 받을 수 있음(물론 실무에서는 성능 테스트를 통해 최적의 스레드 숫자를 찾는것이 이상적임)
  • 결국 스레드 숫자만 늘리면 성능 개선이 되는데, 이런 부분을 잘 이해하지 못하여 서버 장비에 문제가 있다고 생각하고 2배 더 좋은 장비로 구매하는 사태가 발생하기도 함(유사한 사례가 많이 발생한다고 함)
  • 이렇게 되면 CPU는 4%의 절반인 2%만 사용하고 사용자는 여전히 동시에 4명 밖에 받지 못하는 사태가 벌어짐
  • 즉, 스레드의 숫자는 CPU-바운드 작업이 많은지, I/O-바운드 작업이 많은가에 따라 다르게 설정 해야 하며 웹 애플리케이션 서버라도 상황에 따라 CPU 바운드 작업이 많을 수 있으므로 애플리케이션 상황에 따라 스레드 숫자를 고려하면 됨
  • CPU-바운드 작업
    • CPU 코어 수 + 1
    • CPU를 거의 100% 사용하는 작업이므로 스레드를 CPU 숫자에 최적화
  • I/O-바운드 작업
    • CPU 코어 수 보다 많은 스레드를 생성, CPU를 최대한 사용할 수 있는 숫자까지 스레드 생성
    • CPU를 많이 사용하지 않으므로 성능 테스트를 통해 CPU를 최대한 활용하는 숫자까지 스레드를 생성
    • 너무 많은 스레드를 생성하면 컨텍스트 스위칭 비용도 함께 증가하기 때문에 적절한 성능 테스트가 필요함
728x90