[쓰레드와 쓰레드 풀]

변우영·2024년 10월 25일

💻 쓰레드와 쓰레드 풀


📝 Summary

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

장점

  • 쓰레드를 생성/수거하는 데 비용이 들지 않으며, 시스템 자원을 줄일 수 있습니다.

단점

  • 쓰레드 풀에 너무 많은 쓰레드를 생성해 두었다가 사용하지 않으면 메모리 낭비가 발생할 수 있습니다.

🚀 0. 프로세스와 쓰레드란?

🏢 0-1. 프로세스 = 프로그램 실행의 주체

프로세스는 독립적인 실행 공간을 갖고 있으며, 다른 프로세스와 자원을 공유하지 않습니다. 프로세스는 시스템 자원을 할당받고 운영체제에서 개별적으로 실행되며, 각 프로세스는 자신만의 메모리 영역을 갖습니다.

  • 비유: 상가 건물(컴퓨터)에서 개별 상점(프로세스)이 독립적으로 운영되는 모습과 비슷합니다.
  • 예시: 컴퓨터에서 카카오톡이나 브라우저의 각 창을 실행하는 것은 각각 독립된 프로세스입니다.
  • 확인 방법: 맥북의 "활성 상태 보기"에서 CPU 사용량 확인 가능.

👨‍💼 0-2. 쓰레드 = 프로세스 내 작업 수행 단위

쓰레드는 프로세스 내에서 실제 작업을 수행하는 단위로, 자원을 공유하여 보다 효율적으로 여러 작업을 처리할 수 있습니다. 쓰레드는 필요할 때 생성되고, 프로세스 내 자원을 공유하여 독립적으로 작업을 수행합니다. 자원을 공유하는 점에서 메모리와 CPU 자원을 효율적으로 사용할 수 있습니다.

  • 비유: 치킨집에서 직원이 치킨을 튀기고 주문을 받고 서빙을 하는 작업을 각자 나눠 하는 것처럼, 프로세스 내에서 쓰레드가 역할을 분담해 작업을 처리합니다.
  • 예시: 웹 서버의 응답 처리를 위한 쓰레드, 백그라운드 데이터 처리 쓰레드 등이 있을 수 있습니다.
  • 확인 방법: "활성 상태 보기 > 프로세스 상세"에서 평균 쓰레드 개수를 확인할 수 있습니다.

⚙️ 1. 쓰레드

1-1. 프로세스와 쓰레드의 차이점

프로세스

  • 프로세스는 운영체제에서 개별적으로 메모리와 자원을 할당받는 프로그램의 단위로, 각 프로세스는 별도의 Code, Data, Heap, Stack 영역을 갖고 있어 독립적입니다.
  • 프로세스 간 통신은 IPC를 통해 이루어지며, 비용이 많이 들기 때문에 큰 오버헤드를 발생시킬 수 있습니다.

쓰레드

  • 쓰레드는 프로세스 내에서 실행되는 작업의 흐름으로, 다른 쓰레드와 Stack을 제외한 메모리를 공유하여 보다 빠르고 효율적인 작업 처리가 가능합니다.

📌 프로세스로만 작업 처리 시 문제점

  1. 높은 오버헤드: 프로세스 생성과 종료에는 많은 자원이 소모됩니다.
  2. 낮은 통신 효율: 프로세스 간 통신에는 IPC 기법이 필요하여 비용이 큽니다.
  3. 낮은 자원 활용: 각 프로세스가 독립적인 자원을 사용하기 때문에 자원 사용이 비효율적입니다.

📌 쓰레드가 해결한 문제들

  • 작은 실행 단위로서 프로세스보다 빠르게 생성 및 종료가 가능합니다.
  • 빠른 컨텍스트 스위칭: 공유 메모리 덕분에 컨텍스트 스위칭 비용이 줄어듭니다.
  • 빠른 통신: 쓰레드는 공유 메모리를 통해 다른 쓰레드와 통신이 쉽습니다.

📌 프로세스와 쓰레드 비교

  • 프로세스는 Code, Data, Heap, Stack을 독립적으로 할당받아 다른 프로세스의 자원에 접근할 수 없습니다.
  • 쓰레드는 Stack을 제외한 모든 메모리를 공유해 빠른 통신이 가능합니다.

1-2. 프로세스와 쓰레드의 주소 공간

  • 프로세스 주소공간:

    • 코드 영역(Code): 프로그램 코드가 저장되는 영역
    • 데이터 영역(Data): 프로그램이 실행되며 사용하는 환경 변수, 파일 등
    • 스택(Stack): 함수 호출 시 반환 주소나 지역 변수 저장
    • 힙(Heap): 동적 할당된 데이터 저장
  • 쓰레드 주소공간:

    • 독립적 Stack 영역: 각 쓰레드마다 독립적으로 할당되어 함수 호출 시 사용하는 공간.
    • 공유 메모리 영역: Code, Data, Heap은 모든 쓰레드가 공유하여 빠른 통신을 가능하게 합니다.

1-3. 멀티 프로세스와 멀티 쓰레드

멀티 프로세스

  • 여러 개의 프로세스가 독립적으로 실행되며 각자 할당된 메모리 공간을 갖습니다. 프로세스 하나가 문제를 일으켜도 다른 프로세스에 영향을 주지 않지만, 프로세스 간 통신이 어렵습니다.
  • 예시: 웹 브라우저에서 각각의 탭을 별도의 프로세스로 실행하여 한 탭이 죽더라도 다른 탭에는 영향을 미치지 않음.

멀티 쓰레드

  • 하나의 프로세스 내에서 여러 쓰레드를 생성하여 동시에 작업을 수행합니다. 쓰레드 간에 Code, Data, Heap을 공유하기 때문에 빠른 통신이 가능합니다. 그러나 하나의 쓰레드가 문제를 일으키면 전체 프로세스에 영향을 줄 수 있습니다.
  • 예시: 한 웹 서버 프로세스 내에서 다수의 클라이언트 요청을 각 쓰레드가 처리하도록 분산하여 빠르게 응답.
종류멀티 프로세스멀티 쓰레드
장점프로세스 문제 발생 시 다른 프로세스에 영향 없음자원 관리 효율성 및 빠른 통신 가능
단점IPC 통신 복잡성, 오버헤드 발생동기화 문제, 전체 프로세스에 영향 가능

🧵 2. 쓰레드 풀

🔄 2-1. 쓰레드 풀의 개념

쓰레드 풀은 자주 발생하는 작업을 위해 미리 일정 수의 쓰레드를 생성해 두고, 작업이 발생할 때마다 풀에서 대기 중인 쓰레드를 할당해 작업을 처리하는 기법입니다. 쓰레드를 매번 생성하고 제거하는 오버헤드를 줄여서 효율적으로 작업을 처리합니다.

  • 비유: 치킨집에서 미리 직원을 고용해 대기시키고, 주문이 들어오면 바로 업무에 투입하여 효율적으로 처리하는 모습과 유사합니다.

📌 쓰레드 풀의 동작 과정

  1. 초기화: 풀의 크기와 작업 큐를 설정합니다.
  2. 작업 수신: 작업이 발생하면 이를 큐에 추가하여 대기 상태가 됩니다.
  3. 작업 수행: 대기 중인 쓰레드가 작업을 가져와 처리하며, 작업 큐는 일반적으로 FIFO 방식으로 처리됩니다.
  4. 작업 완료 및 반환: 작업이 완료되면 쓰레드는 다시 대기 상태로 돌아가 다음 작업을 기다립니다.
  5. 종료: 더 이상 작업이 없으면 풀을 종료하여 자원을 해제합니다.

2-2. 쓰레드 풀을 사용하는 이유

  • 프로그램 성능 최적화: 매번 쓰레드를 생성/소거하는 오버헤드를 줄여 프로그램 전체 성능을 높입니다.
  • 다수의 사용자 요청을 효율적으로 처리: 웹 서버에서 다수의 요청을 동시에 처리할 때 효율적으로 대응할 수 있습니다.

2-3. 쓰레드 풀의 장단점

  • 장점

    • 쓰레드 생성/소거 비용 절감: 매번 새 쓰레드를 생성하고 제거하지 않아도 되므로 비용이 줄어듭니다.
    • 자원 절약: 풀에 생성된 쓰레드를 재사용해 시스템 자원을 절약할 수 있습니다.
  • 단점

    • 메모리 낭비 가능성: 대기 중인

    쓰레드가 많아질 경우 메모리를 낭비하게 됩니다.

2-4. 동시성(Concurrency)과 병렬성(Parallelism)

  • 동시성 (Concurrency): 단일 코어에서 여러 스레드가 번갈아 실행되며 멀티태스킹처럼 보이는 방식입니다. 실제로는 한 번에 하나의 작업을 수행하지만 빠르게 전환합니다.
  • 병렬성 (Parallelism): 멀티 코어에서 여러 스레드가 동시에 실행되며 실제 병렬 처리됩니다.
    • 데이터 병렬성: 데이터를 나눠 각 쓰레드에서 병렬로 처리.
    • 작업 병렬성: 서로 다른 작업을 각기 다른 쓰레드에서 동시에 처리.

📌 쓰레드 풀 요약

작업 처리에 사용되는 쓰레드를 제한된 개수로 관리하며, 작업 큐에서 할당된 작업을 효율적으로 처리하는 기법입니다. I/O 작업과 데이터베이스 작업에서 효율성을 극대화하는 데 사용됩니다.

💡 3. 쓰레드 풀의 다양한 활용 및 구체적 예시

쓰레드 풀은 높은 처리 속도가 필요한 시스템에서 특히 많이 사용됩니다. 대표적인 쓰레드 풀 사용 분야는 웹 서버, 데이터베이스 연결 관리, 그리고 백그라운드 작업 처리입니다. 각 분야에서 쓰레드 풀은 자원 낭비를 줄이고, 응답 속도를 개선하는 데 핵심적인 역할을 합니다.


🌐 3-1. 쓰레드 풀의 실제 사용 예시

1) 웹 서버에서의 쓰레드 풀

웹 서버는 다수의 사용자 요청을 동시에 처리해야 하므로, 쓰레드 풀은 효율적인 요청 관리에 필수적입니다. 요청이 들어올 때마다 쓰레드를 새로 생성하는 대신, 미리 준비된 쓰레드가 각 요청을 빠르게 처리할 수 있습니다.

  • 설명: 웹 서버는 설정된 수의 쓰레드를 미리 생성해두고, 각 쓰레드는 큐에 있는 작업(클라이언트 요청)을 가져와 처리합니다. 작업이 끝나면 쓰레드는 다시 대기 상태로 돌아가 다음 요청을 받을 준비를 합니다.
  • 장점: 자원 낭비를 줄이면서도 빠른 응답을 유지할 수 있습니다. 각 요청에 즉시 응답하여 서버의 효율성을 극대화할 수 있습니다.
JavaScript 웹 서버 예시

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);
    });
});

2) 데이터베이스 연결 관리에서의 쓰레드 풀

데이터베이스 작업은 시스템 자원을 많이 소모하므로 쓰레드 풀을 통해 효율적으로 관리할 수 있습니다. 데이터베이스 쓰레드 풀을 사용하면 새로운 연결을 생성하는 대신, 쓰레드를 통해 빠르게 요청을 처리하여 서버 부하를 줄이고 응답 속도를 개선합니다.

  • 설명: 데이터베이스 쓰레드 풀은 미리 설정된 수의 쓰레드를 생성해두고, 각 쓰레드가 데이터베이스 요청을 처리하게 합니다. 이를 통해 데이터베이스 서버에 가해지는 부하를 줄이고, 응답 속도를 높일 수 있습니다.
  • 장점: 다수의 데이터베이스 요청이 동시에 발생할 때 자원을 최적화하여, 성능을 극대화할 수 있습니다.
JavaScript 데이터베이스 예시

간단한 데이터베이스 쿼리 처리를 위한 쓰레드 풀 예시입니다

// 데이터베이스 요청을 처리하는 쓰레드 풀 사용
const dbPool = new ThreadPool(5); // 5개의 쓰레드가 있는 풀 생성

function queryDatabase(query) {
    dbPool.runTask(() => {
        // 쿼리 작업 처리
        executeQuery(query);
    });
}

3) 백그라운드 작업 처리에 쓰레드 풀 활용

로그 기록, 파일 처리와 같은 백그라운드 작업도 쓰레드 풀을 통해 비동기적으로 처리할 수 있습니다. 이를 통해 메인 프로세스는 사용자 요청에 집중하고, 쓰레드 풀은 백그라운드 작업을 처리하여 서버의 안정성을 유지할 수 있습니다.

  • 설명: 로그 파일 쓰기, 이미지 변환, 주기적인 데이터 백업 등의 작업을 쓰레드 풀에서 비동기로 처리해, 메인 프로세스의 부하를 줄입니다.
  • 장점: 서버의 응답 속도를 유지하면서, 백그라운드 작업을 빠르게 처리할 수 있어 시스템 안정성을 높입니다.
JavaScript 백그라운드 작업 예시

간단한 로그 파일 작성 작업을 위한 쓰레드 풀 예시입니다

// 로그 파일 작성 작업을 위한 쓰레드 풀 생성
const logPool = new ThreadPool(3); // 3개의 쓰레드가 있는 풀

function logToFile(logData) {
    logPool.runTask(() => {
        // 로그 데이터를 파일에 쓰기
        writeLogToFile(logData);
    });
}

📝 쓰레드 풀의 주요 활용 분야

  • 웹 서버: 여러 사용자 요청을 빠르게 처리해 서버 응답 시간을 최적화합니다.
  • 데이터베이스 관리: 다수의 데이터베이스 요청을 효율적으로 처리하여 서버 부하를 줄이고 응답 속도를 높입니다.
  • 백그라운드 작업: 로그 기록이나 파일 처리 등의 비동기 작업을 빠르게 처리하며, 메인 프로세스에 부담을 주지 않습니다.
profile
개발자로 한걸음!

0개의 댓글