학습
C++ C++에서의 멀티스레딩 기초
C++ C++에서의 멀티스레딩 기초 — 중급
C++에서의 멀티스레딩 기초
1. 도입 (왜 배워야 하는가)
C++에서 멀티스레딩은 현대 소프트웨어 개발에서 필수적인 기술입니다. 멀티스레딩을 사용하면 CPU의 여러 코어를 효과적으로 활용하여 프로그램의 성능을 극대화할 수 있습니다. 예를 들어, 대규모 데이터 처리, 웹 서버, 게임 개발 등에서는 동시에 여러 작업을 처리할 수 있어야 합니다.
만약 멀티스레딩을 모른다면, CPU의 성능을 100% 활용하지 못하고, 프로그램이 지연되거나 느려지며, 사용자가 불만을 가질 수 있습니다. 또한, 멀티스레딩 환경에서 발생할 수 있는 동기화 문제를 해결하지 못하면 데이터 손실이나 경합 상태(race condition)와 같은 심각한 버그가 발생할 수 있습니다.
2. 핵심 개념 설명
- 스레드(Thread): 프로그램 내에서 독립적으로 실행되는 흐름입니다. 여러 스레드가 동시에 실행될 수 있습니다.
- 동기화(Synchronization): 여러 스레드가 공유 자원에 접근할 때 충돌을 방지하는 과정입니다. 예를 들어, 여러 사람이 동시에 은행 계좌에 접근할 때, 한 사람이 돈을 인출하는 동안 다른 사람이 접근하지 못하도록 막는 것입니다.
- 뮤텍스(Mutex): 여러 스레드가 동시에 자원에 접근하지 못하도록 잠그는 도구입니다. 문을 잠그고 누군가가 들어올 수 없게 하는 것과 비슷합니다.
| 용어 | 정의 |
|---|---|
| 스레드 | 독립적으로 실행되는 프로그램의 흐름 |
| 동기화 | 여러 스레드 간의 데이터 충돌 방지 과정 |
| 뮤텍스 | 스레드 간의 자원 접근을 제어하는 도구 |
3. 기본 코드 예제
다음은 C++에서 기본 스레드를 생성하는 코드입니다.
#include <iostream>
#include <thread>
void printHello() {
std::cout << "안녕하세요, 스레드!" << std::endl;
}
int main() {
std::thread t1(printHello); // 새로운 스레드 t1 생성, printHello 함수 실행
t1.join(); // 메인 스레드는 t1이 끝날 때까지 대기
return 0;
}
실행 결과
안녕하세요, 스레드!
코드 설명
#include <iostream>: 입력 및 출력을 위한 헤더 파일을 포함합니다.#include <thread>: 스레드를 사용하기 위한 헤더 파일을 포함합니다.void printHello(): 문자열을 출력하는 함수입니다.std::thread t1(printHello): 새로운 스레드를 생성하고printHello함수를 실행합니다.t1.join(): 메인 스레드는 t1 스레드가 종료될 때까지 기다립니다.- 문제:
t1.join()없이 메인을 종료하면 스레드가 종료되지 않을 수 있습니다. - 해결: 반드시
join()을 호출하여 스레드가 종료될 때까지 기다립니다. - 문제: 여러 스레드가 동시에 접근하면 데이터가 손상될 수 있습니다.
- 해결: 접근할 때마다 뮤텍스를 사용하여 동기화합니다.
- 멀티스레딩은 CPU 자원을 효율적으로 활용하여 성능을 높이는 기술입니다.
- 스레드, 동기화, 뮤텍스 개념을 이해하는 것이 중요합니다.
- 실무에서는 웹 서버 요청 처리, 데이터 병렬 처리와 같은 패턴을 자주 사용합니다.
- 다음에 학습하면 좋은 주제로는 스레드 풀(Thread Pool), 비동기 프로그래밍(Asynchronous Programming), C++의 조건 변수(Condition Variable)를 추천합니다.
4. 실전 활용 예제
1. 웹 서버의 요청 처리
쇼핑몰 웹 서버에서 여러 고객의 요청을 동시에 처리할 수 있어야 합니다. 각 요청을 스레드로 처리하면, 한 고객의 요청이 다른 고객의 요청을 지연시키지 않도록 할 수 있습니다.
#include <iostream>
#include <thread>
void handleRequest(int clientId) {
std::cout << "고객 " << clientId << "의 요청을 처리 중..." << std::endl;
}
int main() {
std::thread clients[5];
for (int i = 0; i < 5; ++i) {
clients[i] = std::thread(handleRequest, i);
}
for (int i = 0; i < 5; ++i) {
clients[i].join();
}
return 0;
}
2. 데이터 처리
대량의 데이터를 처리할 때, 각 스레드가 데이터의 일부를 처리하도록 하여 전체 처리 시간을 단축할 수 있습니다.
#include <iostream>
#include <thread>
#include <vector>
void processData(std::vector<int>& data, int start, int end) {
for (int i = start; i < end; ++i) {
data[i] *= 2; // 데이터 처리 (예: 값 두 배)
}
}
int main() {
std::vector<int> data(1000, 1); // 1000개의 값이 1인 벡터
std::thread t1(processData, std::ref(data), 0, 500);
std::thread t2(processData, std::ref(data), 500, 1000);
t1.join();
t2.join();
std::cout << "처리 완료!" << std::endl;
return 0;
}
5. 흔한 실수와 주의사항
실수 1: 스레드 종료를 기다리지 않음
std::thread t1(printHello);
실수 2: 뮤텍스 사용을 잊음
std::cout << sharedResource << std::endl; // 공유 자원 접근