쓰레드는 프로세스 내에서 실행되는 실행 단위이며, 쓰레드 풀은 쓰레드를 미리 생성해 두어 작업 처리에 사용되는 쓰레드를 제한된 개수만큼 정해 놓고 작업 큐에 들어오는 작업들을 하나씩 쓰레드가 맡아 처리하는 기법입니다.
프로세스는 독립적인 실행 공간을 갖고 있으며, 다른 프로세스와 자원을 공유하지 않습니다. 프로세스는 시스템 자원을 할당받고 운영체제에서 개별적으로 실행되며, 각 프로세스는 자신만의 메모리 영역을 갖습니다.
쓰레드는 프로세스 내에서 실제 작업을 수행하는 단위로, 자원을 공유하여 보다 효율적으로 여러 작업을 처리할 수 있습니다. 쓰레드는 필요할 때 생성되고, 프로세스 내 자원을 공유하여 독립적으로 작업을 수행합니다. 자원을 공유하는 점에서 메모리와 CPU 자원을 효율적으로 사용할 수 있습니다.
프로세스 주소공간:
쓰레드 주소공간:
| 종류 | 멀티 프로세스 | 멀티 쓰레드 |
|---|---|---|
| 장점 | 프로세스 문제 발생 시 다른 프로세스에 영향 없음 | 자원 관리 효율성 및 빠른 통신 가능 |
| 단점 | IPC 통신 복잡성, 오버헤드 발생 | 동기화 문제, 전체 프로세스에 영향 가능 |
쓰레드 풀은 자주 발생하는 작업을 위해 미리 일정 수의 쓰레드를 생성해 두고, 작업이 발생할 때마다 풀에서 대기 중인 쓰레드를 할당해 작업을 처리하는 기법입니다. 쓰레드를 매번 생성하고 제거하는 오버헤드를 줄여서 효율적으로 작업을 처리합니다.
장점
단점
쓰레드가 많아질 경우 메모리를 낭비하게 됩니다.
작업 처리에 사용되는 쓰레드를 제한된 개수로 관리하며, 작업 큐에서 할당된 작업을 효율적으로 처리하는 기법입니다. I/O 작업과 데이터베이스 작업에서 효율성을 극대화하는 데 사용됩니다.
쓰레드 풀은 높은 처리 속도가 필요한 시스템에서 특히 많이 사용됩니다. 대표적인 쓰레드 풀 사용 분야는 웹 서버, 데이터베이스 연결 관리, 그리고 백그라운드 작업 처리입니다. 각 분야에서 쓰레드 풀은 자원 낭비를 줄이고, 응답 속도를 개선하는 데 핵심적인 역할을 합니다.
웹 서버는 다수의 사용자 요청을 동시에 처리해야 하므로, 쓰레드 풀은 효율적인 요청 관리에 필수적입니다. 요청이 들어올 때마다 쓰레드를 새로 생성하는 대신, 미리 준비된 쓰레드가 각 요청을 빠르게 처리할 수 있습니다.
Node.js의 쓰레드 풀을 활용한 간단한 웹 서버 예시입니다
// threadPool.js 파일
const { Worker } = require('worker_threads');
class ThreadPool {
constructor(size) {
this.size = size;
this.pool = [];
this.queue = [];
for (let i = 0; i < size; i++) {
this.pool.push(this.createWorker());
}
}
createWorker() {
const worker = new Worker('./worker.js');
worker.on('message', (result) => {
console.log('결과:', result);
if (this.queue.length > 0) {
const task = this.queue.shift();
worker.postMessage(task);
} else {
this.pool.push(worker);
}
});
return worker;
}
runTask(task) {
if (this.pool.length > 0) {
const worker = this.pool.pop();
worker.postMessage(task);
} else {
this.queue.push(task);
}
}
}
module.exports = ThreadPool;
// app.js 파일
const ThreadPool = require('./threadPool');
const pool = new ThreadPool(5); // 5개의 쓰레드를 가진 풀
// 웹 서버의 각 요청을 쓰레드 풀에서 처리
server.on('request', (req, res) => {
pool.runTask(() => {
handleRequest(req, res);
});
});
데이터베이스 작업은 시스템 자원을 많이 소모하므로 쓰레드 풀을 통해 효율적으로 관리할 수 있습니다. 데이터베이스 쓰레드 풀을 사용하면 새로운 연결을 생성하는 대신, 쓰레드를 통해 빠르게 요청을 처리하여 서버 부하를 줄이고 응답 속도를 개선합니다.
간단한 데이터베이스 쿼리 처리를 위한 쓰레드 풀 예시입니다
// 데이터베이스 요청을 처리하는 쓰레드 풀 사용
const dbPool = new ThreadPool(5); // 5개의 쓰레드가 있는 풀 생성
function queryDatabase(query) {
dbPool.runTask(() => {
// 쿼리 작업 처리
executeQuery(query);
});
}
로그 기록, 파일 처리와 같은 백그라운드 작업도 쓰레드 풀을 통해 비동기적으로 처리할 수 있습니다. 이를 통해 메인 프로세스는 사용자 요청에 집중하고, 쓰레드 풀은 백그라운드 작업을 처리하여 서버의 안정성을 유지할 수 있습니다.
간단한 로그 파일 작성 작업을 위한 쓰레드 풀 예시입니다
// 로그 파일 작성 작업을 위한 쓰레드 풀 생성
const logPool = new ThreadPool(3); // 3개의 쓰레드가 있는 풀
function logToFile(logData) {
logPool.runTask(() => {
// 로그 데이터를 파일에 쓰기
writeLogToFile(logData);
});
}