스레드 풀

섬네일|400px|대기 중인 태스크(파란색)와 완료된 태스크(노란색)가 있는 샘플 스레드 풀(녹색 상자)

컴퓨터 프로그래밍에서 **스레드 풀**(Thread pool)은 컴퓨터 프로그램에서 병행 실행을 달성하기 위한 소프트웨어 디자인 패턴이다. 종종 **복제 작업자** 또는 **작업자 그룹 모델**이라고도 불리는 스레드 풀은 감독 프로그램에 의해 병행 실행을 위해 할당될 태스크를 기다리는 여러 스레드를 유지한다. 스레드 풀을 유지함으로써 이 모델은 성능을 향상시키고 짧은 수명 태스크를 위해 스레드를 자주 생성하고 파괴하는 데 따른 실행 지연을 방지한다. 또 다른 장점은 사용 가능한 스레드보다 적은 스레드를 사용할 때 시스템 부하를 제한하는 기능이다. 사용 가능한 스레드 수는 실행 완료 후 병렬 태스크 큐와 같이 프로그램에 사용 가능한 컴퓨팅 리소스에 맞춰 조정된다.

## 성능
스레드 풀의 크기는 태스크 실행을 위해 예비로 유지되는 스레드 수이다. 일반적으로 애플리케이션의 튜닝 가능한 매개변수이며, 프로그램 성능을 최적화하기 위해 조정된다. 최적의 스레드 풀 크기를 결정하는 것은 성능 최적화에 중요하다.

각 태스크에 대해 새 스레드를 생성하는 것보다 스레드 풀의 한 가지 이점은 스레드 생성 및 파괴 오버헤드가 풀의 초기 생성으로 제한되어 더 나은 성능과 더 나은 시스템 안정성을 가져올 수 있다는 것이다. 스레드 및 관련 리소스를 생성하고 파괴하는 것은 시간 측면에서 비용이 많이 드는 프로세스일 수 있다. 그러나 예비 스레드 수가 과도하면 메모리가 낭비되고, 실행 가능한 스레드 간의 컨텍스트 스위칭은 성능 저하를 초래한다. 많은 CPU 사이클이 소요될 수 있는 다른 네트워크 호스트와의 소켓 연결은 두 개 이상의 네트워크 트랜잭션 과정에서 유지되는 스레드와 연결하여 더 효율적으로 유지할 수 있다.

스레드 시작 시간을 제쳐두고서라도 스레드 풀을 사용하는 것이 유용할 수 있다. 수동으로 스레드를 관리할 때보다 훨씬 쉽게 작업을 큐에 넣고, 병행성을 제어하며, 스레드를 더 높은 수준에서 동기화할 수 있는 스레드 풀 구현이 있다. 이 경우 사용의 성능 이점은 부차적일 수 있다.

일반적으로 스레드 풀은 단일 컴퓨터에서 실행된다. 그러나 스레드 풀은 전체 처리량을 늘리기 위해 스레드 풀 자체일 수 있는 마스터 프로세스가 다른 컴퓨터의 작업자 프로세스에 작업을 분배하는 서버 팜과 개념적으로 관련이 있다. 처치 곤란 병렬 문제는 이 접근 방식에 매우 적합하다.

스레드 수는 대기 중인 태스크 수에 따라 애플리케이션 수명 동안 동적으로 조정될 수 있다. 예를 들어, 웹 서버는 많은 웹 페이지 요청이 들어오면 스레드를 추가할 수 있고, 요청이 줄어들면 스레드를 제거할 수 있다. 더 큰 스레드 풀을 갖는 비용은 리소스 사용량 증가이다. 스레드를 생성하거나 파괴할 시기를 결정하는 데 사용되는 알고리즘은 전체 성능에 영향을 미친다.
* 너무 많은 스레드를 생성하면 리소스가 낭비되고 사용되지 않는 스레드를 생성하는 데 시간이 소요된다.
* 너무 많은 스레드를 파괴하면 나중에 다시 생성할 때 더 많은 시간이 필요하다.
* 너무 느리게 스레드를 생성하면 클라이언트 성능이 저하될 수 있다(긴 대기 시간).
* 너무 느리게 스레드를 파괴하면 다른 프로세스의 리소스가 부족해질 수 있다.

## 언어별 구현
배시에서는 예를 들어 Xargs의 `--max-procs` / `-P`로 구현된다.

```bash
# Fetch 5 URLs in parallel
urls=(
    "https://example.com/file1.txt"
    "https://example.com/file2.txt"
    "https://example.com/file3.txt"
    "https://example.com/file4.txt"
    "https://example.com/file5.txt"
)

printf '%s\n' "${urls[@]}" | xargs -P 5 -I {} curl -sI {} | grep -i "content-length:"
```


Go에서는 워커 풀이라고 불린다.

```go
package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= numJobs; a++ {
        <-results
    }
}
```


다음과 같이 출력된다.

```console
$ time go run worker-pools.go
worker 1 started  job 1
worker 2 started  job 2
worker 3 started  job 3
worker 1 finished job 1
worker 1 started  job 4
worker 2 finished job 2
worker 2 started  job 5
worker 3 finished job 3
worker 1 finished job 4
worker 2 finished job 5

real    0m2.358s
```


## 같이 보기

* 비동기성 (컴퓨터 프로그래밍)
* 객체 풀 패턴
* 병행성 패턴
* 그랜드 센트럴 디스패치
* 병렬 확장 (.NET)
* 병렬화
* 서버 팜
* 스테이지형 이벤트 중심 아키텍처

## 각주

## 외부 링크
* "Query by Slice, Parallel Execute, and Join: A Thread Pool Pattern in Java" by Binildas C. A.
* "Thread pools and work queues" by Brian Goetz
* "A Method of Worker Thread Pooling" by Pradeep Kumar Sahu
* "Work Queue" by Uri Twig: C++ code demonstration of pooled threads executing a work queue.
* "Windows Thread Pooling and Execution Chaining"
* "Smart Thread Pool" by Ami Bar
* "Programming the Thread Pool in the .NET Framework" by David Carmona
* "Creating a Notifying Blocking Thread Pool in Java" by Amir Kirsh
* "Practical Threaded Programming with Python:  Thread Pools and Queues" by Noah Gift
* "Optimizing Thread-Pool Strategies for Real-Time CORBA" by Irfan Pyarali, Marina Spivak, 더글러스 C. 슈미트 and Ron Cytron
* "Deferred cancellation. A behavioral pattern" by Philipp Bachmann
* "A C++17 Thread Pool for High-Performance Scientific Computing" by Barak Shoshany

분류:병렬 컴퓨팅
분류:소프트웨어 디자인 패턴
분류:병행 컴퓨팅
분류:스레드