std::thread 사용법 정리 (C++17 기준)
1. 기본 개념
- C++11부터 도입된 std::thread는 병렬 실행을 위한 클래스입니다.
- 쓰레드를 생성하면 자동으로 시작되며, 작업이 끝날 때까지 join() 또는 detach() 호출로 관리해야 합니다.
2. 기본 예제
#include <iostream>
#include <thread>
void say_hello() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(say_hello); // 쓰레드 시작
t.join(); // 메인 쓰레드가 t를 기다림
}
- join()이 없으면 프로그램이 끝나기 전에 쓰레드가 종료되지 않을 수 있어 런타임 오류 발생.
3. 인자 전달
void print_sum(int a, int b) {
std::cout << a + b << std::endl;
}
int main() {
std::thread t(print_sum, 3, 4);
t.join();
}
- 값은 복사되어 전달됩니다.
- 참조를 넘기려면 std::ref() 사용
void modify(int& x) {
x += 10;
}
int main() {
int value = 5;
std::thread t(modify, std::ref(value));
t.join();
std::cout << value << std::endl; // 15
}
4. 람다 함수 사용
std::thread t([](){
std::cout << "Running in a lambda thread!" << std::endl;
});
t.join();
5. 멤버 함수 호출
class Worker {
public:
void run() {
std::cout << "Worker running!" << std::endl;
}
};
int main() {
Worker w;
std::thread t(&Worker::run, &w); // 객체 포인터 전달
t.join();
}
6. join() vs detach()
- join(): 쓰레드가 끝날 때까지 기다림
- detach(): 쓰레드를 백그라운드에서 실행시키고 제어를 넘김
std::thread t([]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Detached thread done" << std::endl;
});
t.detach(); // join하지 않아도 됨
주의: detach한 쓰레드는 더 이상 제어할 수 없으며, 메인 함수가 먼저 종료되면 문제가 생길 수 있음.
7. 쓰레드 ID 확인
8. 쓰레드 관련 함수들
- std::this_thread::sleep_for(duration): 일정 시간 동안 sleep
- std::this_thread::yield(): 다른 쓰레드에게 CPU 양보
- std::thread::hardware_concurrency(): 시스템의 논리 코어 수 반환
9. 예외 처리
std::thread 자체는 예외를 전달하지 않기 때문에, 예외는 쓰레드 내부에서 처리해야 합니다.
std::thread t([]() {
try {
throw std::runtime_error("error!");
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}
});
t.join();
10. C++17에서 유용한 추가 기능
- std::scoped_lock을 쓰레드와 뮤텍스에 함께 사용 가능
- std::shared_mutex (읽기/쓰기 락)과 함께 사용 시 성능 최적화 가능
- 쓰레드 안전한 std::atomic 변수와 병행 사용
std::shared_mutex란?
- 동시에 여러 개의 쓰레드가 읽을 수 있도록 허용하지만,
- 쓰기(수정) 시에는 단 하나의 쓰레드만 접근 가능하게 만드는 락입니다.
읽기: 병렬 허용
쓰기: 단독만 허용예: std::mutex와 std::shared_mutex의 차이
일반 std::mutex 사용 시
cppstd::mutex mtx; void read() {
std::lock_guard<std::mutex> lock(mtx); // 읽기 작업
}→ 읽기조차 직렬화됨 (읽기끼리도 동시에 접근 불가)
std::shared_mutex 사용 시#include <shared_mutex> std::shared_mutex shared_mtx; void read() { std::shared_lock<std::shared_mutex> lock(shared_mtx); // 여러 쓰레드 동시에 가능 // 읽기 작업 } void write() { std::unique_lock<std::shared_mutex> lock(shared_mtx); // 단독 접근 // 쓰기 작업 }
→ 여러 쓰레드가 동시에 읽기 가능
→ 쓰기 쓰레드는 단독 접근만 허용 (읽기 중인 쓰레드가 끝날 때까지 대기)
성능 최적화의 이유
쓰기보다 읽기가 많은 상황 (예: 캐시, 설정 조회 등)에서는:
- std::mutex: 모든 작업이 직렬 → 비효율적
- std::shared_mutex: 읽기 병렬화 가능 → 성능 향상
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>
std::shared_mutex shared_mtx;
int shared_data = 0;
void reader(int id) {
std::shared_lock<std::shared_mutex> lock(shared_mtx);
std::cout << "Reader " << id << " read value: " << shared_data << std::endl;
}
void writer(int value) {
std::unique_lock<std::shared_mutex> lock(shared_mtx);
shared_data = value;
std::cout << "Writer wrote value: " << value << std::endl;
}
int main() {
std::vector<std::thread> threads;
// 읽기 쓰레드 여러 개
for (int i = 0; i < 5; ++i)
threads.emplace_back(reader, i);
// 쓰기 쓰레드 하나
threads.emplace_back(writer, 100);
for (auto& t : threads) t.join();
}
'Develop > C&CPP' 카테고리의 다른 글
[cpp] atomic (0) | 2025.04.20 |
---|---|
[cpp] mutex, lock (0) | 2025.04.20 |
[cpp] cpp17에서 달라진 점 (0) | 2025.04.20 |
[cpp] cpp14에서 추가된 것 (0) | 2025.04.20 |
[service] 윈도우 서비스 프로그램 (0) | 2025.01.05 |