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

Remove ads
성능
스레드 풀의 크기는 태스크 실행을 위해 예비로 유지되는 스레드 수이다. 일반적으로 애플리케이션의 튜닝 가능한 매개변수이며, 프로그램 성능을 최적화하기 위해 조정된다.[3] 최적의 스레드 풀 크기를 결정하는 것은 성능 최적화에 중요하다.
각 태스크에 대해 새 스레드를 생성하는 것보다 스레드 풀의 한 가지 이점은 스레드 생성 및 파괴 오버헤드가 풀의 초기 생성으로 제한되어 더 나은 성능과 더 나은 시스템 안정성을 가져올 수 있다는 것이다. 스레드 및 관련 리소스를 생성하고 파괴하는 것은 시간 측면에서 비용이 많이 드는 프로세스일 수 있다. 그러나 예비 스레드 수가 과도하면 메모리가 낭비되고, 실행 가능한 스레드 간의 컨텍스트 스위칭은 성능 저하를 초래한다. 많은 CPU 사이클이 소요될 수 있는 다른 네트워크 호스트와의 소켓 연결은 두 개 이상의 네트워크 트랜잭션 과정에서 유지되는 스레드와 연결하여 더 효율적으로 유지할 수 있다.
스레드 시작 시간을 제쳐두고서라도 스레드 풀을 사용하는 것이 유용할 수 있다. 수동으로 스레드를 관리할 때보다 훨씬 쉽게 작업을 큐에 넣고, 병행성을 제어하며, 스레드를 더 높은 수준에서 동기화할 수 있는 스레드 풀 구현이 있다.[4][5] 이 경우 사용의 성능 이점은 부차적일 수 있다.
일반적으로 스레드 풀은 단일 컴퓨터에서 실행된다. 그러나 스레드 풀은 전체 처리량을 늘리기 위해 스레드 풀 자체일 수 있는 마스터 프로세스가 다른 컴퓨터의 작업자 프로세스에 작업을 분배하는 서버 팜과 개념적으로 관련이 있다. 처치 곤란 병렬 문제는 이 접근 방식에 매우 적합하다.
스레드 수는 대기 중인 태스크 수에 따라 애플리케이션 수명 동안 동적으로 조정될 수 있다. 예를 들어, 웹 서버는 많은 웹 페이지 요청이 들어오면 스레드를 추가할 수 있고, 요청이 줄어들면 스레드를 제거할 수 있다. 더 큰 스레드 풀을 갖는 비용은 리소스 사용량 증가이다. 스레드를 생성하거나 파괴할 시기를 결정하는 데 사용되는 알고리즘은 전체 성능에 영향을 미친다.
- 너무 많은 스레드를 생성하면 리소스가 낭비되고 사용되지 않는 스레드를 생성하는 데 시간이 소요된다.
- 너무 많은 스레드를 파괴하면 나중에 다시 생성할 때 더 많은 시간이 필요하다.
- 너무 느리게 스레드를 생성하면 클라이언트 성능이 저하될 수 있다(긴 대기 시간).
- 너무 느리게 스레드를 파괴하면 다른 프로세스의 리소스가 부족해질 수 있다.
Remove ads
언어별 구현
요약
관점
배시에서는 예를 들어 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
Remove ads
같이 보기
각주
외부 링크
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads