[CS] Thread와 Thread 풀

말랑이·2025년 2월 19일

CS

목록 보기
8/10

Summary

스레드는 프로세스 내에서 실행되는 실행단위이며,
스레드 풀은 스레드를 미리 생성해두어 작업처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 기법이다.

스레드 풀의 장점은 스레드를 생성/수거하는데 비용이 들지 않고, 스레드 풀을 미리 만들어두기 때문에 시스템 자원을 줄일 수 있다.

스레드 풀의 단점으로는 스레드 풀에 스레드를 너무 많이 생성해 두었다가 사용하지 않으면 메모리 낭비가 발생한다.


1. 프로세스와 스레드

💡 프로세스 : 자신만의 고유공간과 자원을 할당받아 사용
💡 스레드 : 다른 스레드와 공간 및 자원을 공유하면서 사용

1️⃣ 프로그램을 실행하는 주체 → 프로세스

  • 상가건물을 컴퓨터라고 한다면, 치킨을 만드는 치킨집을 프로세스라고 할 수 있음
  • 상가안의 식당들은 배정된 자신의 공간과 돈이 있음 (👉🏻 필요한 만큼 배정된 고유공간)
  • ex) 카카오톡을 실행하는 프로세스

2️⃣ 작업을 처리해주는 주체 → 스레드

  • 치킨집 안에 실제 업무를 분담해 수행하는 직원을 스레드라고 할 수 있음
  • 이 직원들은 치킨집 안의 공간과 재료를 같이 씀 (👉🏻 자원 공유)
  • ex) 카카오톡 메시지 발송을 처리하는 스레드, 메시지 수신을 처리하는 스레드 등
  • 스레드는 필요할 때 마다 생성됨

2. 스레드

1️⃣ 프로세스와 스레드의 차이점

📌 프로세스

  • 자신만의 고유공간과 자원을 할당받아 사용함
  • 프로세스는 메모리 상에서 실행중인 프로그램을 말하고, 스레드는 이 프로세스 안에서 실행되는 흐름단위
  • 프로세스는 최소 하나 이상의 스레드를 보유하고 있고, 각각 별도의 주소공간을 독립적으로 할당받음 (code, heap, stack)
💡 프로세스 만으로 작업을 처리할 때의 문제점
	- 프로세스 생성에 큰 오버헤드가 있음
    - 프로세스를 생성할 때 많은 시간이 소요됨
    - 프로세스 컨텍스트 스위칭의 비효율성이 큼
    - 프로세스 사이에 통신이 어려움

📌 스레드

  • 다른 스레드와 공간과 자원을 공유하면서 사용함
  • 스레드는 프로세스 안에서 작업을 처리하는 주체로 자원 중에 stack만 따로 할당받고, 나머지 영역은 서로 공유함
💡 스레드가 해결한 문제들
	- 프로세스보다 크기가 작은 실행단위 구현
    - 프로세스 생성 및 소멸에 따른 오버헤드 감소
    - 스레드 간 자원을 공유함으로써 빠른 컨텍스트 스위칭 가능
    - 프로세스들의 통신시간 및 방법의 어려움 해소

📌 프로세스와 스레드 비교

  • 운영체제는 프로세스마다 독립된 메모리 영역을 Code/Data/Stack/Heap의 형식으로 할당함
    • 👉🏻 각각 독립된 메모리 영역을 할당해주기 때문에 프로세스는 다른 프로세스의 변수나 자료에 접근할 수 없음
  • 이와 다르게 스레드는 메모리를 서로 공유할 수 있음
    • 👉🏻 프로세스가 할당받은 메모리 영역 내에서 Stack은 따로 할당받고, 나머지 Code/Data/Heap은 영역을 서로 공유함
    • 👉🏻 즉, 치킨집에서 각자 일하는 공간이 있지만, 같이 보는 업무가이드(Code)가 있고, 주문도 같이 받고(Data), 손님이 오고가는 홀 공간도 같이 쓴다(Heap)

2️⃣ 프로세스와 스레드의 주소공간

📌 프로세스의 주소공간

구분내용
사전적 정의- 컴퓨터에서 연속적으로 실행하고 있는 컴퓨터 프로그램
- 메모리에 올라와 실행되고 있는 프로그램의 인스턴스 (독립적 개체)
- 운영체제로부터 시스템 자원을 할당받는 자원의 단위
프로세스의 특징- 각각 독립된 메모리 영역을 할당받음
- 최소 1개 이상의 스레드(Main)를 가짐
- 각 프로세스는 별도의 주소공간에서 실행되며 다른 프로세스 자원에 접근할 수 없음
- 여러 프로세스가 동시에 실행되고 관리되는 것처럼 보이나, CPU는 한번에 한가지 명령밖에 처리하지 못함.
(즉, 동시가 아니라 빠르게 프로세스들을 번갈아가며 실행하고 관리하는 것)

📌 스레드의 주소공간

구분내용
사전적 정의- 프로세스 내에서 실행되는 여러 흐름의 단위
- 프로세스의 특정한 수행경로
- 프로세스가 할당받은 자원을 이용하는 최소 실행 단위
스레드의 특징- 프로세스 내에서 각 필요한 Stack만 할당받고, Code/Data/Heap 영역은 스레드끼리 공요함
- 스레드는 공유자원인 Heap의 변수를 수정할 수 있음
💡 스레드가 독립적으로 가지고 있는 부분 → 작업 흐름과 관련됨
	- Program Counter (실행할 명령어)
    - Register set
    - Stack space
💡 스레드가 공유하는 부분 → 작업 데이터와 관련됨
	- Code section
    - Data section
    - OS resource

3️⃣ 멀티 프로세스와 멀티 스레드

💡 멀티 프로세스 : 하나의 운영체제 안에서 여러 프로세스가 실행되는 것
💡 멀티 스레드 : 하나의 프로세스가 여러 작업을 여러 스레드를 사용해 동시에 처리하는 것

종류멀티 프로세스멀티 스레드
장점- 하나의 프로세스가 죽어도 다른 프로세스에 영향을 끼치지 않음- 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리 가능
- Code/Data/Heap 영역을 공유하기 때문에 데이터를 주고 받는 것이 간단해지고, 자원 소모가 적음
- 스레드 사이 작업량이 작아 문맥교환이 빠르며 시스템 처리량 증가
단점- 각각 독립된 메모리 영역을 가지고 있어 작업량이 많을수록 오버헤드가 발생하고 문맥교환으로 인한 성능저하 유발
- 프로세스 사이의 통신이 복잡함(IPC)
- 프로그램 디버깅이 어려움 (에러추적)
- 하나의 스레드에 문제가 생기면 전체적인 프로세스에 영향을 끼침
- 동기화 문제 발생 (전역변수를 사용하기 때문에)
- 단일 프로세스 시스템에서 효과를 기대하기 어려움
- 다른 프로세스에서 스레드 제어 불가

📌 멀티 프로세스

  • 하나의 프로그램을 여러개의 프로세스가 구성하여 각 프로세스가 작업(Task)을 처리하도록 하는 기법 (👉🏻 멀티태스킹과 비슷한 의미)
[특징]
- 여러개의 자식 프로세스 중 하나에 문제가 생겨도, 다른 자식 프로세스는 영향을 받지 않음
- Context Switching 과정에서 캐시메모리 초기화 등 무거운 작업으로 오버헤드가 발생함
- 프로세스 간 공유하는 메모리가 없어서 Context Switching이 발생하면 데이터를 처음부터 불러와야 함

[참고]
- 문맥교환은 CPU 코어를 여러 프로세스가 돌아가면서 사용해서 작업을 처리하는 방법
- 해당 프로세스의 상태를 자신의 PCB에 저장하고 대기하고 있던 다음순서의 프로세스가 이전 프로세스 상태를 복구하는 작업

📌 멀티 스레드

  • 하나의 응용프로그램을 여러개의 스레드로 구성하고 각 스레드가 하나의 작업을 처리하도록 하는 기법
  • 윈도우, 리눅스 등 많은 OS는 멀티프로세싱도 지원하지만 멀티스레딩을 기본으로 함
[특징]
- 시스템 자원 소모 감소 (→ 자원의 효율성)
  👉🏻 프로세스를 생성해 자원을 할당하는 시스템 콜이 줄어서 자원을 효율적으로 관리할 수 있음
- 시스템 처리량 감소 (→ 처리비용 감소)
  👉🏻 스레드 간 데이터를 주고받는 것이 간단해지면서 시스템 자원소모가 줄어듬
  👉🏻 스레드 사이의 작업량이 작아 문맥교환이 활발하게 일어남

[단점]
- Thread-Safety에 문제가 없도록 확실한 설계가 필요함
- 디버깅이 까다로움
- 동기화 및 교착상태가 발생할 수 있음
  👉🏻 둘 이상의 스레드가 서로의 작업이 끝나길 기다리며 작업을 더이상 진행하지 못하는 상태
- 프로세스 밖에서 해당 스레드를 제어할 수 없음
- 하나의 스레드가 문제가 생기면 프로세스 전체가 영향을 받음

❓ 멀티 프로세스 대신 멀티 스레드를 사용하는 이유

자원 효율성 증대처리비용 감소응답시간 단축
프로세스 생성 시 자원을 할당하는 시스템 콜이 줄어들어 자원소모가 적고 자원을 효율적으로 관리할 수 있다.
또한 프로세스의 경우 Context Switching 시 CPU 레지스터, RAM과 CPU 사이의 캐시메모리가 초기화되기 때문에 오버헤드가 큰 반면, 스레드는 Context Switching시 Stack 영역만 처리하면 되므로 쓰레드 간의 문맥교환 속도가 빠르다.
그리고 프로세스의 경우 프로세스 간의 통신을 위해서는 IPC를 통해서 통신을 해야 하지만, 스레드의 경우 스레드 간의 자원 공유가 간단하기 때문에 시스템 자원 소모가 작다.

그러나 전역변수를 통해 스레드 간 자원을 공유하기 때문에 동기화문제는 잘 해결해야 한다.

⚠️ 멀티 스레드 동기화 문제

멀티스레드를 사용하면 각각의 스레드 중, 어떤 것이 어떤 순서로 실행될지 알 수 없다.
만약 A스레드가 자원을 사용하다가 B스레드로 제어권이 넘어간 후, B스레드가 해당자원을 수정했을 때
다시 제어권을 받은 A가 해당자원에 접근하지 못하거나 바뀐 자원에 접근하게 되는 오류가 발생할 수 있다.

즉, 여러 스레드가 함께 전역변수를 사용할 경우 발생할 수 있는 충돌.
스케줄링은 운영체제가 해결해주지 않기 때문에 프로그래머가 적절한 기법을 직접 구현해야함.


2. 스레드 풀

작업처리에 사용되는 스레드를 제한된 개수만큼 정해놓고, 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 디자인패턴

  • 프로그램이 작업을 동시에 실행할 수 있도록, 여러 스레드를 미리 생성해두고 유지관리함
  • 스레드를 동시에 만들어 실행 (👉🏻 병렬처리)
  • 스레드를 제한없이 무한으로 늘리는 것은 하드웨어의 제한적인 사항 때문에 위험하여 스레드 풀이라는 개념을 이용함
💡 스레드 풀의 동작
  ① 초기화
    - 스레드 풀을 사용하기 전에 초기화해야 함
    - 스레드 풀의 크기, 최대 스레드 수, 작업 큐 등의 매개변수를 설정함
  ② 작업수신
    - 스레드 풀은 작업을 수신하고 처리할 준비를 함
    - 작업은 일반적으로 작업 큐에 추가
  ③ 작업수행
    - 스레드 풀에서는 미리 생성된 스레드들이 작업 큐를 모니터링하고, 대기중인 작업을 가져와 처리함
    - 스레드 풀 내의 스레드들은 일반적으로 무한루프를 돌면서 작업을 가져오기 위해 대기함
  ④ 작업처리
    - 스레드 풀의 스레드가 작업을 가져와 처리함
    - 작업 큐에서 FIFO(선입선출) 방식으로 가져오게 됨
  ⑤ 작업완료 및 반환
    - 작업이 완료되면 해당결과를 반환하고, 스레드는 다시 작업 큐에서 새로운 작업을 가져오기 위해 대기상태로 돌아감
  ⑥ 작업대기
    - 작업 큐에 새로운 작업이 추가되면 스레드 풀의 스레드들은 대기상태를 벗어나 작업을 가져와 처리함
    - 이를 반복하여 계속적으로 작업 수행
  ⑦ 종료
    - 스레드 풀을 더이상 사용하지 않을 때 종료함
    - 종료할 때는 모든 작업이 완료되었는지 확인하고, 필요에 따라 남은 작업들을 처리하거나 버릴 수 있음

1️⃣ 스레드 풀을 사용하는 이유

📌 프로그램 성능저하를 방지하기 위해

  • 매번 발생하는 작업을 병렬처리하기 위해 스레드를 생성/수거하는 데에 따른 부담은 프로그램 전체적인 퍼포먼스를 저하시킴
  • 스레드 또한 프로세스가 할당한 메모리를 사용함
  • Java의 경우 스레드를 생성하면 JVM 메모리를 소비하게 되는 것
  • 스레드 자체도 레지스터와 스택을 가지고, 스레드도 컨텍스트 스위칭이 일어나기 때문에 스레드 생성에 따른 메모리 할당을 무시할 수 없음

📌 다수의 사용자 요청을 처리하기 위해

  • 대규모 프로젝트에서 사용됨
  • 다수의 사용자 요청을 수용하고, 빠르게 처리하고 대응하기 위해 스레드 풀을 사용함
  • 특히 Bottle Neck 현상이 발생하는 I/O 작업과 데이터베이스 작업에서 주로 사용됨
  • 스레드가 아무리 빠르게 생성되더라도 시스템 스케줄러에서 스레드의 우선순위를 매번 할당해야 하는데, 스레드 풀을 사용하면 일정 스레드가 스스로 생성되기 때문에 스레드 풀에 의해 라이프사이클 관리가 되고, 작업이 큐를 이용하게 되어 우선순위가 배분됨

2️⃣ 스레드 풀의 장단점

📌 스레드 풀의 장점

  • 스레드를 생성/수거하는데 비용이 들지 않음
  • 스레드가 생성될 때 OS가 메모리 공간을 확보해주고, 메모리를 스레드에게 할당함
  • 스레드 풀을 미리 만들어 두기 때문에 처음에 생성하는 비용은 들지만 이전의 스레드를 재사용할 수 있으므로 시스템 자원을 줄일 수 있음
  • 작업 요청 시 이미 스레드가 대기중인 상태이기 때문에 작업을 실행하는데 딜레이가 발생하지 않음

📌 스레드 풀의 단점

  • 스레드를 너무 많이 생성해두었다가 사용하지 않으면 메모리 낭비가 발생함
  • 단점 개선 : Fork join thread pool
    (큰 업무를 작은 업무로 나누어 배분 → 작업 완료 후 취합한 형태)

3️⃣ 동시성과 병렬성

📌 동시성 Concurrency

  • 싱글코어에서 멀티스레드를 동작시키기 위한 방식
  • 멀티태스킹을 위해 여러개의 스레드가 번갈아가면서 실행되는 성질
  • 동시성을 이용한 싱글코어의 멀티태스킹은 각 스레드들이 병렬적으로 실행되는 것처럼 보이지만, 사실은 번갈아가면서 조금씩 실행되고 있는 것

📌 병렬성

  • 멀티코어에서 멀티스레드를 동작시키는 방식
  • 한개 이상의 스레드를 포함하는 각 코어들이 동시에 실행되는 성질
💡 데이터 병렬성
	- 전체 데이터를 쪼개 서브 데이터들로 만든 뒤, 서브데이터들을 병렬처리하여 작업을 빠르게 수행하는 것
    - Java 8의 병렬 스트림
    - 서브데이터는 멀티코어의 수만큼 쪼개어 각각의 데이터들을 분리된 스레드에서 병렬처리함

💡 작업 병렬성
	- 서로 다른 작업을 병렬처리 하는 것
    - 웹서버는 각각의 브라우저에서 요청한 내용을 개별스레드에서 병렬로 처리함

참고

profile
🐰 I'm Sunyeon-Jeong, mallang

0개의 댓글