기본 패턴
cpp
#include <thread>
#include <mutex>
std::mutex mtx;
int counter = 0;
void increment() {
std::lock_guard<std::mutex> lock{mtx}; // 잠금 (범위 종료 시 자동 해제)
++counter;
}
int main() {
std::thread t1{increment};
std::thread t2{increment};
t1.join(); // t1 완료 대기
t2.join(); // t2 완료 대기
}설명
스레드 생성과 join / detach
cpp
#include <thread>
#include <iostream>
void task(int id) {
std::cout << "스레드 " << id << " 실행\n";
}
// 함수 포인터
std::thread t1{task, 1};
// lambda
std::thread t2{[](int id){ std::cout << id; }, 2};
// join — 스레드가 끝날 때까지 호출 스레드가 대기
t1.join();
// detach — 호출 스레드와 분리, 독립적으로 실행
// t2.detach(); // join 또는 detach 중 하나는 반드시 호출해야 함
t2.join();mutex — 공유 데이터 보호
여러 스레드가 같은 데이터를 동시에 수정하면 데이터 경쟁(data race) 이 발생합니다.
cpp
#include <mutex>
std::mutex mtx;
int shared = 0;
void safeIncrement() {
mtx.lock();
++shared; // 한 번에 하나의 스레드만 진입
mtx.unlock();
}
// ❌ lock/unlock 직접 호출 — 예외 발생 시 unlock 누락 위험lock_guard / unique_lock — RAII 방식 잠금
cpp
// lock_guard — 간단한 잠금, 범위 벗어나면 자동 해제
void safe1() {
std::lock_guard<std::mutex> lock{mtx};
++shared;
} // 여기서 자동 unlock
// unique_lock — 잠금/해제를 수동 제어하거나 조건 변수와 함께 사용
void safe2() {
std::unique_lock<std::mutex> lock{mtx};
++shared;
lock.unlock(); // 명시적 해제 가능
// 잠금 없이 다른 작업...
lock.lock(); // 다시 잠금
}조건 변수 — 스레드 간 신호
cpp
#include <condition_variable>
#include <queue>
std::mutex qmtx;
std::condition_variable cv;
std::queue<int> dataQueue;
// 생산자
void producer() {
std::lock_guard<std::mutex> lock{qmtx};
dataQueue.push(42);
cv.notify_one(); // 대기 중인 스레드 하나 깨우기
}
// 소비자
void consumer() {
std::unique_lock<std::mutex> lock{qmtx};
cv.wait(lock, []{ return !dataQueue.empty(); }); // 조건 만족까지 대기
int val = dataQueue.front();
dataQueue.pop();
}std::async / std::future — 결과값을 받는 비동기 작업
cpp
#include <future>
int heavyWork(int n) {
return n * n;
}
// async로 비동기 실행 후 future로 결과 수신
std::future<int> result = std::async(std::launch::async, heavyWork, 10);
// 다른 작업 수행...
int value = result.get(); // 완료까지 대기 후 값 반환 (100)빠른 정리
| 항목 | 역할 |
|---|---|
std::thread | 스레드 생성 |
.join() | 스레드 완료까지 대기 (반드시 호출) |
.detach() | 스레드를 독립 실행으로 분리 |
std::mutex | 상호 배제 잠금 |
std::lock_guard | RAII 잠금 (범위 기반 자동 해제, 권장) |
std::unique_lock | 수동 제어 가능한 잠금, 조건 변수와 함께 사용 |
std::condition_variable | 스레드 간 이벤트 신호 |
std::async | 비동기 함수 실행, 결과는 future로 |
std::future::get() | 비동기 결과 수신 (완료까지 대기) |
주의할 점
std::thread 객체가 소멸될 때 join()도 detach()도 호출하지 않으면 std::terminate()가 호출됩니다. 반드시 둘 중 하나를 호출하세요.
데이터 경쟁은 컴파일러 경고 없이 발생합니다. 공유 데이터에 접근하는 모든 경로에서 동일한 mutex로 보호해야 합니다. 락을 잡은 채로 다른 락을 잡으면 교착 상태(deadlock) 가 발생할 수 있습니다.
참고 링크
1 sources