✅들어가면서
프로세스와 스레드의 개념을 배우면서 스레드는 프로세스 내에 존재하는 실행 단위이며, 이 스레드가 여러 개인 덕분에 우리가 음악을 들으며 웹서핑을 하거나 파일을 다운 받는 등의 '동시 작업'이 가능하다라고 배웠다. 그래서 나 또한 스레드가 많을 수록 동시에 처리할 수 있는 작업의 수가 증가하는 것이니, 당연히 멀티 스레드가 무조건 성능이 더 좋다고 생각했다. 과연 멅티 스레드가 성능이 제일 좋은 것인지 지금부터 알아보자.
⭐스레드를 많이 쓰면 쓸 수록 성능이 좋아질까?
결론부터 말하면 모든 상황에 대해 무조건 멀티 스레드가 싱글 스레드보다 무조건 좋다고 말할 순 없다. 그렇다면 싱글 스레드와 멀티 스레드 두 가지 모델을 비교하고 언제 어느 때에 무엇이 더 좋은지, 왜 멀티 스레드가 항상 성능이 좋지 않은지에 대해 알아보도록 하자.
1️⃣ 임계 영역에 대한 동기화 비교
멀티 스레드는 자원을 공유하기 때문에 프로세스 생성에 비해 적은 메모리와 자원을 소모하고 컨텍스트 스위칭도 멀티 프로세스에 비해 빠르다는 장점이 있다. 하지만 여러 개의 스레드가 임계 영역(Critical Section)의 공유 자원에 접근할 수 있기 때문에, 데이터의 일관성과 정확성을 유지하기 위해 동기화(Synchronized) 기법을 사용하여야 한다.

임계 영역(Critical Section)
멀티 스레드 프로그래밍에서 임계 영역은 공유 자원을 접근하는 코드 영역을 말한다. 대표적으로 전역 변수나 heap 메모리 영역을 들 수 있다.
동기화 기법에는 대표적으로 뮤텍스(Mutex)와 세마포어(Semaphore) 같은 잠금 기법이 있다. 이들은 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 제한해야 하여 데이터에 대한 일관성을 유지한다. 그러나 이러한 동기화 기법은 스레드 간의 경쟁과 대기 상황을 발생시키므로, 오히려 성능에 부정적인 영향을 미칠 수 있다.
또한 CPU 캐시와 메모리 사이의 캐시 데이터 일관성 문제도 발생할 수 있는데, 여러 개의 스레드가 동시에 공유하는 메모리나 데이터에 대해 수정을 가할 때 특정 CPU 코어의 캐시에 저장된 데이터와 다른 CPU 코어의 캐시에 저장된 데이터가 일치하지 않는 경우가 발생한다. 이럴 경우, 다른 CPU 코어에서 변경한 데이터가 현재 CPU 코어의 캐시에 반영되지 않은 상태로 사용되어 문제가 발생할 수 있어, CPU 캐시에서 데이터를 불러오는 비용이 발생하므로 성능에 영향을 미치게 된다.
2️⃣컨텍스트 스위칭 오버헤드
컨텍스트 스위칭 오버헤드(context switching overhead)는 여러 개의 프로세스나 스레드가 있을 때, CPU가 현재 프로세스나 스레드의 상태를 저장하고 다른 프로세스나 스레드로 전환될 때 발생하는 비용을 말한다. 컨텍스트 스위칭 하는 과정에서 CPU 시간과 자원을 소모하므로 성능에 영향을 미치는 것이다.
우리가 멀티 프로세스 대신 멀티 스레드로 프로그램 모델을 구성하는 이유는 프로세스의 컨텍스트 스위칭 오버헤드 보다 스레드의 컨텍스트 스위칭 오버헤드가 훨씬 작아 병목이 적기 때문이다. 하지만 어디까지나 프로세스에 비해 상대적으로 작다는 것이지 오버헤드 자체 비용은 결코 무시할 수는 없다.
즉, 싱글 스레드 모델에서는 스레드가 한 개이니 컨텍스트 스위칭 오버헤드가 발생되지 않지만, 멀티 스레드 모델은 스레드가 여러 개이니 컨텍스트 스위칭 오버헤드가 발생하게 되고, 스레드가 많으면 많을 수록 스위칭 횟수도 많아지고 덩달아 오버헤드도 많아져 성능이 저하될 수 있다는 의미이다. 특히, 싱글 코어와 같은 옛날 CPU와 같이 코어 수는 적은데 스레드 수를 계속 늘리게 되면, 각 코어에서 경합하는 스레드 수가 점점 많아질 거고, 어느 순간에는 오버헤드 때문에 성능 한계에 부딪히게 될 수 있게 된다.
3️⃣잔여 스레등의 리소스 낭비
특정 회사의 서비스 이용률이 24시간 항상 바쁜 상태이지는 않을 것이다. 즉, 멀티 스레드 어플리케이션에서 이용률이 한산하여 스레드를 한두개 밖에 이용하지 않을 때, 나머지 잔여 스레드들이 CPU, 메모리, 네트워크 등의 자원을 불필요하게 점유해서 성능 저하나 오류의 원인이 될 수 있게 된다. 즉, 스레드가 작업을 수행하지 않더라도 존재 자체만으로 여전히 리소스를 소비하고 오버헤드를 생성한다는 것이다.
이러한 문제를 해결하기 위해서는, 작업을 수행하지 않는 스레드의 개수를 최소화하고, 스레드 풀(Thread Pool)과 같은 매커니즘을 사용하여 스레드의 개수를 관리하여 리소스 낭비를 최소화하는 것이 중요하다.
스레드 풀이란 나중에 쓰일 예비 스레드들을 담고 있는 상자이다.
4️⃣어플리케이션 성격에 따른 제약
"스레드를 많이 쓰면 쓸수록 동시에 더 많은 작업들을 실행할 수 있다"라는 말에는 우리도 모르게 암묵적으로 해당 어플리케이션은 더 작은 작업들로 잘게 쪼개서 동시에 실행이 가능한 성격의 어플리케이션이라는 전제를 깔고 생각한다. 즉, 어플리케이션의 목적과 주제에 따라 아키텍쳐가 달라질 수 있다는 얘기인데, 예를들어 만약 개발하는 어플리케이션의 동작이 순차적으로 실행돼야만 하는 특징을 가지거나 잘게 쪼개서 동시에 실행하기에 매우 어려운 성격의 어플리케이션이라면 오히려 멀티 스레드 구성은 별 이점이 없다는 것이다. 이에 대한 대표적인 예시로는 CPU 바운드 어플리케이션과 I/O 바운드 어플리케이션을 예로 들 수 있다.
- CPU 바운드 어플리케이션
- 오라클에서 아키텍트로 일하고 있는 Goetz가 2002, 2006년에 발표한 자바 병렬 프로그래밍에 관한 책과 논문에 따르면 CPU 바운드 작업에서 적절한 스레드 수는 코어 수 + 1 이라고 주장했었다. 이는 위에서 살펴보았던 컨텍슽트 스위칭 오버헤드 비용 때문이라는 걸 유추할 수 있을 것이다.
- 따라서 CPU 바운드 어플리케이션은 CPU를 많이 쓰기 때문에 코어 수와 비슷한 수준 이상으로 스레드 수를 늘려봤자 별 이점이 없으며, 오히려 각 코어에서 스레드 수가 많아질 수록 컨텍스트 스위칭 때문에 오버헤드만 많아져서 성능이 안 좋은 영향을 주게 된다. 그래서 스레드의 개수가 적절하게 설정되어야 최적의 성능을 얻을 수 있는 것이다.
- I/O 바운드 어플리케이션
- 파일 입출력, 네트워크 통신, 데이터베이스 조회와 같은 입출력 작업이 많을 경우 스레드를 늘리는 것이 좋다. 이러한 작업들은 I/O 장치의 응답 속도에 의존하는 것이 큰데, 예를 들어 파일 입출력은 파일을 읽기 위해 디스크에서 데이터를 조회하는데 있어 하드웨어 한계상 입출력 작업에 걸리는 시간이 상대적으로 오래 걸리므로 이때 CPU는 입출력이 완료될 때까지 휴무 상태가 된다. 그래서 만일 싱글 스레드일 경우 I/O 작업하는 동안 블로킹 되어 낭비가 발생하게 된다.
- 멀티 스레드는 I/O 작업이 처리될 동안 다음에 이행할 작업들을 다른 스레드에게 자원을 할당하여 수행할 수 있도록 할 수 있다. 네트워크 통신도 마찬가지로 네트워크 상황에 따라 전송 시간이 오래 걸릴 수 있으므로 통신 지연동안 스레드를 재활용 시키는 것이다.
- 따라서 I/O 작업이 많은 상황에서는 CPU 코어 수보다 2배, 3배, 혹은 그 이상으로 스레드 수를 늘려주는 것이 코어들을 더 효율적으로 쓸 수 있기 때문에 성능 면에서 이점이 얻을 수 있다. 그러나 우리가 앞서 배웠듯이 스레드를 늘리면 늘릴 수록, 잔여 스레드의 컨텍스트 오버헤드와 동기화 등의 문제점이 발생할 수 있기 때문에, I/O 바운드 어플리케이션에 멀티 스레드 모델 대신 비동기 I/O 처리에 특화된 이벤트 기반 프로그래밍 모델을 접목하기도 한다. 가장 대표적으로 Node.js 어플리케이션을 들 수 있다.
정리하자면 상황과 목적에 맞는 모델을 선택하는 게 좋다!
'네트워크' 카테고리의 다른 글
| 동기/비동기 & 블로킹/논블로킹 (0) | 2026.03.13 |
|---|---|
| 멀티 프로세스 vs 멀티 스레드 (0) | 2026.03.12 |
| 프로세스와 스레드 (1) | 2026.03.12 |