스레드 풀

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

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

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

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

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

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

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

언어별 구현
배시에서는 예를 들어 Xargs의 --max-procs / -P로 구현된다.
# 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에서는 워커 풀이라고 불린다.
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
 }
}

다음과 같이 출력된다.
$ 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

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