스레드 관리의 구조: 멀티스레딩, LWP, 스레드 풀

멀티스레딩의 자원 공유 구조와 LWP의 역할, 스레드 풀 설계, 동기화와 스레드 안전성 문제를 정리한다.

2026-08-14 · 최초 발행 2026-01-16

프로세스 안에서 실행 흐름을 나누는 방법

스레드는 프로세스 내부에서 실행되는 가장 작은 실행 단위다. 프로세스는 최소 하나의 메인 스레드를 가지며, 필요하면 추가 스레드를 만들어 병렬로 작업을 수행할 수 있다.

프로세스와 스레드는 격리 범위와 자원 공유 방식에서 차이가 난다. 프로세스는 독립된 주소 공간을 가지지만, 같은 프로세스에 속한 스레드는 코드·데이터·힙을 함께 사용한다. 대신 각 스레드는 자체 스택과 실행 상태를 유지한다.

프로세스 B스레드 1코드 영역데이터 영역 영역스택 1프로세스 A스레드 1스레드 2코드 영역 (공유)데이터 영역 (공유) 영역 (공유)스택 1스택 2
구분 프로세스(Process) 스레드(Thread)
자원 공유 독립적 주소 공간 코드, 데이터, 힙 공유
생성 비용 높음 (메모리 할당 필요) 낮음 (스택만 할당)
컨텍스트 스위칭 비용 높음 비용 낮음
통신 방법 IPC (파이프, 소켓 등) 공유 메모리 직접 접근
독립성 한 프로세스 오류가 타 프로세스에 영향 없음 한 스레드 오류가 전체 프로세스에 영향

스레드별로 분리되는 정보와 프로세스 단위로 공유되는 정보는 다음과 같다.

구성 요소 설명 공유 여부
스레드 ID 스레드 고유 식별자 개별
프로그램 카운터 다음 실행 명령어 주소 개별
레지스터 집합 CPU 레지스터 값 개별
스택 지역 변수, 함수 호출 정보 개별
상태 정보 Ready, Running, Blocked 등 개별
코드 영역 실행 코드 공유
데이터 영역 전역 변수 공유
힙 영역 동적 할당 메모리 공유
파일/자원 열린 파일, 소켓 등 공유

공유 자원을 쓰는 멀티스레딩의 장점과 부담

멀티스레딩은 하나의 프로세스 안에서 여러 스레드가 동시에 실행되는 방식이다. 각각은 별도 실행 흐름을 갖지만 프로세스 자원을 함께 사용한다.

UI 처리와 작업 처리를 분리해 응답성을 유지할 수 있고, 프로세스 자원을 효율적으로 공유할 수 있다. 프로세스를 새로 만드는 방식보다 스레드 생성 비용이 낮으며, 멀티코어 환경에서는 병렬 처리와 기능별 모듈화에도 적합하다.

반면 공유 자원 접근에는 동기화가 필요하다. 실행 순서가 고정되지 않으므로 버그 재현과 디버깅이 어려울 수 있고, 락을 잘못 사용하면 데드락이 발생한다. 한 스레드의 오류가 같은 프로세스 전체에 영향을 줄 수 있다는 점도 고려해야 한다.

사용자 스레드와 커널 스레드의 연결

스레드 구현은 사용자 수준과 커널 수준으로 나뉜다. 둘을 어떻게 매핑하느냐에 따라 블로킹의 범위, 멀티코어 활용 가능성, 관리 비용이 달라진다.

멀티스레딩 모델다대일 (N:1)일대일 (1:1)다대다 (M:N)커널 수준 스레드커널 스레드 1커널 스레드 2사용자 수준 스레드사용자 스레드 1사용자 스레드 2사용자 스레드 3CPU 코어

다대일 모델 (Many-to-One)

여러 사용자 스레드를 하나의 커널 스레드에 매핑하는 방식이다. 스레드 관리를 사용자 공간에서 수행하므로 효율적이지만, 한 스레드가 블로킹되면 프로세스 전체가 블로킹된다. 멀티코어도 활용할 수 없다. 초기 Solaris Green Threads와 GNU Portable Threads가 예시다.

일대일 모델 (One-to-One)

사용자 스레드마다 커널 스레드를 하나씩 연결한다. 실제 병렬 실행이 가능하고, 한 스레드의 블로킹이 다른 스레드에 영향을 주지 않는다. 다만 커널 스레드를 만드는 오버헤드가 있으며 생성 가능한 스레드 수에 제한이 있다. Windows와 Linux (NPTL)가 이 모델의 예시다.

다대다 모델 (Many-to-Many)

여러 사용자 스레드를 같거나 더 적은 수의 커널 스레드에 매핑한다. 다대일과 일대일 모델의 장점을 결합할 수 있지만 구현은 복잡하다. Solaris LWP와 Windows ThreadFiber가 예시로 제시된다.

LWP가 연결하는 사용자 공간과 커널

경량 프로세스(LWP)는 사용자 수준 스레드와 커널 수준 스레드 사이에 놓이는 계층이다. 커널이 스케줄링하는 실체이면서 사용자 스레드가 매핑되는 대상이다.

커널 공간LWP 계층사용자 공간사용자 스레드 1사용자 스레드 2사용자 스레드 3사용자 스레드 4LWP 1LWP 2커널 스레드 1커널 스레드 2CPU 코어 1CPU 코어 2

LWP는 커널 스케줄러가 직접 관리하며, 사용자 스레드는 LWP 위에서 실행된다. 블로킹 시스템 콜도 LWP 단위로 처리할 수 있어 다대다 모델을 구현하는 핵심 요소가 된다. 프로세스보다 생성과 전환 비용이 가볍다는 점도 특징이다.

구분 사용자 스레드 LWP 커널 스레드
관리 주체 스레드 라이브러리 커널 커널
스케줄링 라이브러리 스케줄러 커널 스케줄러 커널 스케줄러
시스템 콜 프로세스 블로킹 가능 개별 블로킹 개별 블로킹
멀티코어 활용 불가 (다대일) 가능 가능
생성 비용 매우 낮음 중간 높음

작업 요청을 재사용 가능한 워커로 처리하기

스레드 풀은 미리 생성한 스레드 집합에 작업을 배정하는 방식이다. 요청이 들어오면 대기 중인 스레드가 작업을 가져가며, 매번 스레드를 생성하고 없애는 비용을 줄이는 데 목적이 있다.

결과스레드작업작업 요청작업 1작업 2작업 3작업 4대기열Worker 1Worker 2Worker 3완료된 작업
구성 요소 역할 설명
Worker Thread 작업 실행 작업 큐에서 작업을 가져와 실행
Task Queue 작업 대기열 실행 대기 중인 작업 저장
Pool Manager 풀 관리 스레드 수 조절, 작업 분배
Completion Handler 결과 처리 작업 완료 후 결과 처리

스레드 재사용은 생성·소멸 비용을 줄이고, 최대 스레드 수 제한으로 시스템 과부하를 막는 데 도움이 된다. 미리 생성된 스레드가 즉시 작업을 처리할 수 있고, 중앙에서 스레드를 관리하거나 풀 크기를 동적으로 조절할 수도 있다.

그러나 유휴 스레드도 메모리를 점유한다. 작업 큐 관리와 동기화가 필요하며, 작업 간 의존성이 있으면 데드락이 생길 수 있다. 요청이 과도하면 큐 포화도 발생할 수 있다.

워크로드에 맞춘 풀 크기

풀 크기는 작업 성격을 기준으로 정한다. CPU 집약적 작업은 CPU 코어 수를 기준으로 두고 컨텍스트 스위칭을 줄인다. I/O 집약적 작업은 I/O 대기 동안 다른 작업을 수행할 수 있도록 코어 수에 배수를 적용한다. 혼합형은 실제 워크로드를 벤치마크해 조정한다.

YesNoYesNo작업 유형 분석CPU 집약적?스레드 = CPU 코어I/O 집약적?스레드 = CPU 코어 x (1+ 대기시간/처리시간)혼합형: 벤치마크로 결정
작업 유형 권장 스레드 수 이유
CPU 집약적 CPU 코어 수 컨텍스트 스위칭 최소화
I/O 집약적 코어 수 x 배수 I/O 대기 동안 다른 작업 수행
혼합형 벤치마크 기반 조정 실제 워크로드 분석 필요
언어/플랫폼 구현체 특징
Java ExecutorService 풍부한 API, Fork/Join 지원
C++ std::thread pool (C++17) 표준 라이브러리
Python ThreadPoolExecutor concurrent.futures 모듈
.NET ThreadPool CLR 관리 스레드 풀
Node.js Worker Threads libuv 기반

동시 접근을 제어하는 동기화

멀티스레드 환경에서 공유 자원에 동시에 접근하면 경쟁 조건(Race Condition)이 발생할 수 있다. 이를 제어하는 대표적인 방법은 다음과 같다.

기법 특징 사용 사례
뮤텍스 상호 배제, 단일 스레드 접근 임계 영역 보호
세마포어 카운팅 기반, 다중 접근 허용 자원 풀 관리
조건 변수 대기/신호 메커니즘 생산자-소비자 패턴
읽기-쓰기 락 다중 읽기, 단일 쓰기 읽기 위주 작업
원자적 연산 락 없는 동기화 카운터, 플래그

스레드 안전성을 해치는 상황

스레드 안전성은 여러 스레드가 함께 실행되더라도 올바른 동작을 보장하는 성질이다. 경쟁 조건은 실행 순서에 따라 결과가 달라지는 문제이며, 동기화나 원자적 연산으로 다룬다. 데드락은 상호 대기로 작업이 멈추는 상태로, 락 획득 순서를 정하거나 타임아웃을 두는 방식이 대응 방법이다.

라이브락은 상태만 바뀔 뿐 실제 진행이 없는 경우이며 임의 지연이나 재시도가 해결 방법이 될 수 있다. 기아 상태에서는 특정 스레드가 자원을 얻지 못하므로 공정 스케줄링이 필요하다.

문제 설명 해결 방법
경쟁 조건 실행 순서에 따른 결과 차이 동기화, 원자적 연산
데드락 상호 대기로 인한 교착 락 순서 정의, 타임아웃
라이브락 상태만 변하고 진행 없음 임의 지연, 재시도
기아 상태 특정 스레드가 자원 획득 불가 공정 스케줄링

스레드 단위로 분리하는 변수 공간

TLS(Thread Local Storage)는 스레드마다 독립적인 변수 공간을 제공한다. 이를 사용하면 동기화 없이 스레드 안전성을 확보할 수 있다.

언어 TLS 구현 예시
C/C++ __thread, thread_local thread_local int counter;
Java ThreadLocal ThreadLocal counter;
Python threading.local() local.counter = 0
스레드멀티스레딩운영체제LWP스레드 풀