쓰레드: CPU Core의 쓰레드 실행 단위. Unit of Execution이라고도 불림
쓰레드 단위로 나눠진 프로세스의 작업들을 CPU Core가 처리
하나의 프로세스에서 두 가지 이상 작업을 동시에 실행이 가능함
Thread만으로 동시에 여러 작업을 실행시키는 프로그램을 만들기
- 해당 예시의 경우 자바로 만든 프로세스는 요청이 올 때마다 새로운 쓰레드를 생성해서 작업를 처리하고 처리한 후에는 쓰레기를 처리
- 하지만 쓰레드를 이렇게 사용하게 되면 문제점이 여러개가 생김
### 1. Thread 생성 비용
- Thread 자체가 생성 비용이 크기 때문에 요청에 대한 응답시간이 늘어남

자바같은 경우 One-to-One Threading Model로 쓰레드를 생성하게 되는데, 이는 User Thread가 생성시에 OS Thread와 연결해줘야 한다는 의미
사실은 이 유저 쓰레드는 OS Thread에 대한 유저 프로그램 계층에서의 추상화라고 볼 수 있는데 자바는 One-to-One Threading Model이기 때문에 User Thread를 생성하게 되면 OS 커널 레벨에 있는 OS 쓰레드와 꼭 하나가 연결되어야 하는 형태
그렇기 때문에 새로운 쓰레드의 생성은 OS 커널의 작업이 동반된다는 것을 의미함. 이 말인 즉슨, 새로운 쓰레드가 생성될 때 생성 비용이 많이 들게 되고 그에 따라서 작업이 요청할 때마다 새로운 Thread 생성에 대한 요청 처리 시간을 감수해야한다는 의미기도 함
2. CPU의 오버헤드 증가, 메모리 문제 발생 가능성
Process의 처리 속도보다 빠르게 요청이 쏟아져 들어올 경우에는 새로운 쓰레드가 이에 맞춰 계속해서 생산되는 방식이며, 무제한적으로 쓰레드가 쌓이게 될 수밖에 없음
쓰레드가 많이 쌓이게 되면 기본적으로 메모리를 많이 차지하게 되고 곧 메모리 문제 발생 가능성을 높임
이와 동시에 context-switching이 많이 발생할 수밖에 없기 때문에 CPU 오버헤드가 증가하는 문제가 생김
Thread Pool
이러한 쓰레드의 무한정적인 생산을 막기 위해 생긴 개념이 쓰레드 풀
미리 일정 개수의 쓰레드를 생성하여 관리하는 기법
작업이 발생하면 대기 중이 쓰레드 하나를 선택하여 작업 수행
작업이 완료되면 쓰레드는 다시 대기 상태로 돌아가고, 새로운 작업을 할당받을 준비를 함
미리 만들어 놓은 쓰레드를 재사용
쓰레드 생성 및 삭제에 따른 오버헤드를 줄이며, 특정 시점에 동시에 처리할 수 있는 작업의 개수 제한 → 시스템 자원을 효율적으로 관리하고 성능 향상
장점
자원 효율성
정해진 개수의 쓰레드 생성 및 관리(재사용) → 쓰레드 생성 및 삭제에 따른 오버헤드를 줄임
자원의 효율적 관리, 불필요한 자원 소모 방지
응답성 및 처리량 향상
작업을 대기 상태로 유지하여 작업 처리 속도 향상
작업이 발생하면 대기 중인 쓰레드 중 하나를 선택하여 작업을 할당 → 작업 처리를 병렬적으로 진행
작업 제어
동시에 처리할 수 있는 작업의 개수 제한
쓰레드 풀의 크기를 조절하여 시스템 부하 조절, 과도한 작업 요청으로 인한 성능 저하 방지
쓰레드 관리
쓰레드의 생명주기를 관리
쓰레드 풀은 쓰레드의 생성, 재사용, 종료 등을 관리하여 쓰레드의 안전한 운영에 용이
Node.js에서의 쓰레드 풀
노드의 쓰레드 풀 같은 경우 기본적으로 libuv 라이브러리에서 관리하고 있음
각 스레드는 I/O 작업을 처리하고, 작업이 완료되면 콜백함수를 호출하여 결과 반환
쓰레드 풀 기본 설정값
node.js의 기본적인 쓰레드 개수는 4로 설정되어 있으며, 이를 변경하기 위해서는 nodejs의 EntryPoint에서 지정해줘야 함
UV_THREADPOOL_SIZE 설정 방법
Windows CLI
$ env:UV_THREADPOOL_SIZE=<value>$ node test.js
macOS/Linux
$ export UV_THREADPOOL_SIZE <value>$ node test.js
Thread Pool 용도
CPU 또는 I/O intensive 한 작업에 사용
I/O-intensive : DNS, File System
CPU-intensive : Crypto, Zlib
Application and modules : use C ++ add-on
위와 같이 nodejs eventloop 에서 처리하기에 무거운 작업이 자동으로 thread pool 내의 thread 에 위임
예제 코드
import {pbkdf2} from 'crypto'import * as process from 'process'console.log('worker thread number is : ' + process.env.UV_THREADPOOL_SIZE)console.time('Hashing1')pbkdf2('a', 'b', 100_000, 512, 'sha512', ()=>{ console.timeEnd('Hashing1')})console.time('Hashing2')pbkdf2('a', 'b', 100_000, 512, 'sha512', ()=>{ console.timeEnd('Hashing2')})console.time('Hashing3')pbkdf2('a', 'b', 100_000, 512, 'sha512', ()=>{ console.timeEnd('Hashing3')})console.time('Hashing4')pbkdf2('a', 'b', 100_000, 512, 'sha512', ()=>{ console.timeEnd('Hashing4')})console.time('Hashing5')pbkdf2('a', 'b', 100_000, 512, 'sha512', ()=>{ console.timeEnd('Hashing5')})
(1) 쓰레드풀 기본값(4)로 5개 해시함수 실행
worker thread number is : undefined
Hashing3: 351.294ms
Hashing4: 361.309ms
Hashing1: 366.053ms
Hashing2: 371.932ms
Hashing5: 658.325ms
기본값이 4개기 때문에 총 1~4번 해싱작업은 동시에 처리되고 5번은 이후에 쓰레드가 할당되어 처리됨
쓰레드풀 (5)개로 해시함수 5개 실행
# 쓰레드풀 사이즈 5 지정$env:UV_THREADPOOL_SIZE=5
worker thread number is : 5Hashing4: 318.785msHashing5: 328.197msHashing2: 329.408msHashing1: 347.599msHashing3: 360.894ms