스레드는 프로세스 내에서 실행되는 실행단위이며,
스레드 풀은 스레드를 미리 생성해두어 작업처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 기법이다.
스레드 풀의 장점은 스레드를 생성/수거하는데 비용이 들지 않고, 스레드 풀을 미리 만들어두기 때문에 시스템 자원을 줄일 수 있다.
스레드 풀의 단점으로는 스레드 풀에 스레드를 너무 많이 생성해 두었다가 사용하지 않으면 메모리 낭비가 발생한다.
💡 프로세스 : 자신만의 고유공간과 자원을 할당받아 사용
💡 스레드 : 다른 스레드와 공간 및 자원을 공유하면서 사용
💡 프로세스 만으로 작업을 처리할 때의 문제점
- 프로세스 생성에 큰 오버헤드가 있음
- 프로세스를 생성할 때 많은 시간이 소요됨
- 프로세스 컨텍스트 스위칭의 비효율성이 큼
- 프로세스 사이에 통신이 어려움
💡 스레드가 해결한 문제들
- 프로세스보다 크기가 작은 실행단위 구현
- 프로세스 생성 및 소멸에 따른 오버헤드 감소
- 스레드 간 자원을 공유함으로써 빠른 컨텍스트 스위칭 가능
- 프로세스들의 통신시간 및 방법의 어려움 해소


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

| 구분 | 내용 |
|---|---|
| 사전적 정의 | - 프로세스 내에서 실행되는 여러 흐름의 단위 - 프로세스의 특정한 수행경로 - 프로세스가 할당받은 자원을 이용하는 최소 실행 단위 |
| 스레드의 특징 | - 프로세스 내에서 각 필요한 Stack만 할당받고, Code/Data/Heap 영역은 스레드끼리 공요함 - 스레드는 공유자원인 Heap의 변수를 수정할 수 있음 |
💡 스레드가 독립적으로 가지고 있는 부분 → 작업 흐름과 관련됨
- Program Counter (실행할 명령어)
- Register set
- Stack space
💡 스레드가 공유하는 부분 → 작업 데이터와 관련됨
- Code section
- Data section
- OS resource
💡 멀티 프로세스 : 하나의 운영체제 안에서 여러 프로세스가 실행되는 것
💡 멀티 스레드 : 하나의 프로세스가 여러 작업을 여러 스레드를 사용해 동시에 처리하는 것
| 종류 | 멀티 프로세스 | 멀티 스레드 |
|---|---|---|
| 장점 | - 하나의 프로세스가 죽어도 다른 프로세스에 영향을 끼치지 않음 | - 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리 가능 - Code/Data/Heap 영역을 공유하기 때문에 데이터를 주고 받는 것이 간단해지고, 자원 소모가 적음 - 스레드 사이 작업량이 작아 문맥교환이 빠르며 시스템 처리량 증가 |
| 단점 | - 각각 독립된 메모리 영역을 가지고 있어 작업량이 많을수록 오버헤드가 발생하고 문맥교환으로 인한 성능저하 유발 - 프로세스 사이의 통신이 복잡함(IPC) | - 프로그램 디버깅이 어려움 (에러추적) - 하나의 스레드에 문제가 생기면 전체적인 프로세스에 영향을 끼침 - 동기화 문제 발생 (전역변수를 사용하기 때문에) - 단일 프로세스 시스템에서 효과를 기대하기 어려움 - 다른 프로세스에서 스레드 제어 불가 |

[특징]
- 여러개의 자식 프로세스 중 하나에 문제가 생겨도, 다른 자식 프로세스는 영향을 받지 않음
- Context Switching 과정에서 캐시메모리 초기화 등 무거운 작업으로 오버헤드가 발생함
- 프로세스 간 공유하는 메모리가 없어서 Context Switching이 발생하면 데이터를 처음부터 불러와야 함
[참고]
- 문맥교환은 CPU 코어를 여러 프로세스가 돌아가면서 사용해서 작업을 처리하는 방법
- 해당 프로세스의 상태를 자신의 PCB에 저장하고 대기하고 있던 다음순서의 프로세스가 이전 프로세스 상태를 복구하는 작업

[특징]
- 시스템 자원 소모 감소 (→ 자원의 효율성)
👉🏻 프로세스를 생성해 자원을 할당하는 시스템 콜이 줄어서 자원을 효율적으로 관리할 수 있음
- 시스템 처리량 감소 (→ 처리비용 감소)
👉🏻 스레드 간 데이터를 주고받는 것이 간단해지면서 시스템 자원소모가 줄어듬
👉🏻 스레드 사이의 작업량이 작아 문맥교환이 활발하게 일어남
[단점]
- Thread-Safety에 문제가 없도록 확실한 설계가 필요함
- 디버깅이 까다로움
- 동기화 및 교착상태가 발생할 수 있음
👉🏻 둘 이상의 스레드가 서로의 작업이 끝나길 기다리며 작업을 더이상 진행하지 못하는 상태
- 프로세스 밖에서 해당 스레드를 제어할 수 없음
- 하나의 스레드가 문제가 생기면 프로세스 전체가 영향을 받음
자원 효율성 증대 ➕ 처리비용 감소 ➕ 응답시간 단축
프로세스 생성 시 자원을 할당하는 시스템 콜이 줄어들어 자원소모가 적고 자원을 효율적으로 관리할 수 있다.
또한 프로세스의 경우 Context Switching 시 CPU 레지스터, RAM과 CPU 사이의 캐시메모리가 초기화되기 때문에 오버헤드가 큰 반면, 스레드는 Context Switching시 Stack 영역만 처리하면 되므로 쓰레드 간의 문맥교환 속도가 빠르다.
그리고 프로세스의 경우 프로세스 간의 통신을 위해서는 IPC를 통해서 통신을 해야 하지만, 스레드의 경우 스레드 간의 자원 공유가 간단하기 때문에 시스템 자원 소모가 작다.
그러나 전역변수를 통해 스레드 간 자원을 공유하기 때문에 동기화문제는 잘 해결해야 한다.
멀티스레드를 사용하면 각각의 스레드 중, 어떤 것이 어떤 순서로 실행될지 알 수 없다.
만약 A스레드가 자원을 사용하다가 B스레드로 제어권이 넘어간 후, B스레드가 해당자원을 수정했을 때
다시 제어권을 받은 A가 해당자원에 접근하지 못하거나 바뀐 자원에 접근하게 되는 오류가 발생할 수 있다.
즉, 여러 스레드가 함께 전역변수를 사용할 경우 발생할 수 있는 충돌.
스케줄링은 운영체제가 해결해주지 않기 때문에 프로그래머가 적절한 기법을 직접 구현해야함.
작업처리에 사용되는 스레드를 제한된 개수만큼 정해놓고, 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 디자인패턴
💡 스레드 풀의 동작
① 초기화
- 스레드 풀을 사용하기 전에 초기화해야 함
- 스레드 풀의 크기, 최대 스레드 수, 작업 큐 등의 매개변수를 설정함
② 작업수신
- 스레드 풀은 작업을 수신하고 처리할 준비를 함
- 작업은 일반적으로 작업 큐에 추가
③ 작업수행
- 스레드 풀에서는 미리 생성된 스레드들이 작업 큐를 모니터링하고, 대기중인 작업을 가져와 처리함
- 스레드 풀 내의 스레드들은 일반적으로 무한루프를 돌면서 작업을 가져오기 위해 대기함
④ 작업처리
- 스레드 풀의 스레드가 작업을 가져와 처리함
- 작업 큐에서 FIFO(선입선출) 방식으로 가져오게 됨
⑤ 작업완료 및 반환
- 작업이 완료되면 해당결과를 반환하고, 스레드는 다시 작업 큐에서 새로운 작업을 가져오기 위해 대기상태로 돌아감
⑥ 작업대기
- 작업 큐에 새로운 작업이 추가되면 스레드 풀의 스레드들은 대기상태를 벗어나 작업을 가져와 처리함
- 이를 반복하여 계속적으로 작업 수행
⑦ 종료
- 스레드 풀을 더이상 사용하지 않을 때 종료함
- 종료할 때는 모든 작업이 완료되었는지 확인하고, 필요에 따라 남은 작업들을 처리하거나 버릴 수 있음

💡 데이터 병렬성
- 전체 데이터를 쪼개 서브 데이터들로 만든 뒤, 서브데이터들을 병렬처리하여 작업을 빠르게 수행하는 것
- Java 8의 병렬 스트림
- 서브데이터는 멀티코어의 수만큼 쪼개어 각각의 데이터들을 분리된 스레드에서 병렬처리함
💡 작업 병렬성
- 서로 다른 작업을 병렬처리 하는 것
- 웹서버는 각각의 브라우저에서 요청한 내용을 개별스레드에서 병렬로 처리함
참고