학습
C++ C++의 동시성 (Concurrency)
C++ C++의 동시성 (Concurrency) — 고급
C++의 동시성 (Concurrency)
1. 도입 (왜 배워야 하는가)
C++의 동시성은 멀티스레딩과 병렬 프로그래밍을 통해 프로그램의 성능을 극대화할 수 있는 강력한 기법입니다. 현대의 컴퓨터는 다수의 코어를 가진 경우가 많아, 이를 활용하여 작업을 동시에 수행하면 실행 시간을 획기적으로 줄일 수 있습니다. 예를 들어, 대규모 데이터 처리, 게임 개발, 웹 서버의 요청 처리 등 여러 분야에서 동시성을 활용하여 성능을 향상시킵니다.
반면, 동시성을 제대로 이해하지 못하면 데드락, 레이스 컨디션과 같은 문제가 발생할 수 있습니다. 이러한 문제는 프로그램의 비정상 종료나 버그를 발생시키며, 결국 사용자 경험을 저하시킬 수 있습니다.
2. 핵심 개념 설명
스레드 (Thread)
- 스레드는 프로세스 내에서 실행되는 경량의 실행 단위입니다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있습니다.
- 비유: 스레드는 회사의 직원과 같고, 프로세스는 그 회사입니다. 회사가 여러 일을 동시에 처리하기 위해 여러 직원이 필요합니다.
- 여러 스레드가 공유 자원에 접근할 때 충돌을 방지하기 위해 동기화가 필요합니다.
- 비유: 여러 사람이 동시에 한 개의 펜을 사용하고자 할 때, 펜을 사용하는 순서를 정하는 것과 같습니다.
- 두 개 이상의 스레드가 공유 자원에 동시에 접근하여 결과가 예상과 다르게 되는 상황입니다.
- 비유: 두 사람이 동시에 문을 열려고 할 때, 누가 먼저 열지에 따라 결과가 달라지는 상황입니다.
동기화 (Synchronization)
레이스 컨디션 (Race Condition)
| 용어 | 정의 |
|---|---|
| 스레드 | 프로세스 내에서 실행되는 독립적인 실행 단위 |
| 동기화 | 여러 스레드가 공유 자원에 접근할 때 충돌 방지 |
| 레이스 컨디션 | 여러 스레드가 동시에 자원을 접근하여 발생하는 오류 |
3. 기본 코드 예제
아래는 C++의 스레드를 사용하는 기본 예제입니다.
#include <iostream>
#include <thread>
void hello() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(hello); // hello 함수로 새로운 스레드 생성
t.join(); // 메인 스레드가 t가 끝날 때까지 기다림
return 0;
}
실행 결과
Hello from thread!
코드 한 줄씩 동작 설명
#include <iostream>: 입출력을 위한 헤더 파일 포함.#include <thread>: 스레드를 사용하기 위한 헤더 파일 포함.void hello(): 스레드가 실행할 함수 정의.std::cout << "Hello from thread!" << std::endl;: 스레드가 실행될 때 메시지를 출력.int main(): 프로그램의 시작점.std::thread t(hello);: hello 함수를 실행하는 새로운 스레드 t 생성.t.join();: 메인 스레드가 t 스레드의 종료를 기다림.return 0;: 프로그램 종료.- 뮤텍스를 잠그지 않으면 레이스 컨디션이 발생할 수 있습니다.
- join()을 호출하지 않으면 메인 스레드가 종료되기 전에 t1 스레드가 종료되지 않을 수 있습니다.
- C++의 동시성은 멀티스레딩을 통해 프로그램의 성능을 향상시킬 수 있습니다.
- 스레드, 동기화, 레이스 컨디션 등의 개념을 이해하는 것이 중요합니다.
- 뮤텍스 등을 활용해 공유 자원에 대한 접근을 안전하게 처리하는 방법을 익혀야 합니다.
- 다음에 학습하면 좋은 주제로는 "C++의 비동기 프로그래밍"이나 "병렬 알고리즘"을 추천합니다.
4. 실전 활용 예제
예제 1: 쇼핑몰에서 장바구니 업데이트
쇼핑몰에서 사용자가 장바구니에 물건을 추가할 때, 각 사용자의 요청을 동시에 처리해야 합니다.
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
std::mutex mtx; // 동기화를 위한 뮤텍스
std::vector<int> cart; // 장바구니
void add_to_cart(int item) {
mtx.lock(); // 뮤텍스 잠금
cart.push_back(item); // 장바구니에 아이템 추가
mtx.unlock(); // 뮤텍스 잠금 해제
std::cout << "Added item " << item << " to cart." << std::endl;
}
int main() {
std::thread t1(add_to_cart, 1);
std::thread t2(add_to_cart, 2);
t1.join();
t2.join();
std::cout << "Items in cart: ";
for (int item : cart) {
std::cout << item << " ";
}
return 0;
}
예제 2: 데이터 분석
대량의 데이터를 처리하는 경우, 여러 스레드를 활용하여 데이터를 병렬로 분석할 수 있습니다.
#include <iostream>
#include <thread>
#include <vector>
void process_data(int start, int end) {
for (int i = start; i < end; ++i) {
// 데이터 처리 로직 (여기서는 단순 출력)
std::cout << "Processing data " << i << std::endl;
}
}
int main() {
const int dataSize = 100;
std::thread t1(process_data, 0, dataSize/2);
std::thread t2(process_data, dataSize/2, dataSize);
t1.join();
t2.join();
return 0;
}
5. 흔한 실수와 주의사항
실수 1: 뮤텍스 잠금을 잊는 것
// 잘못된 코드
void add_to_cart(int item) {
// mtx.lock(); // 잠금 누락
cart.push_back(item);
// mtx.unlock(); // 잠금 해제 누락
}
// 올바른 코드
void add_to_cart(int item) {
mtx.lock();
cart.push_back(item);
mtx.unlock();
}
실수 2: join() 호출 누락
// 잘못된 코드
std::thread t1(add_to_cart, 1);
// t1.join(); // join 호출 누락
// 올바른 코드
std::thread t1(add_to_cart, 1);
t1.join();