2026년 상식닷컴 선정 식당 & 카페 리스트
최근에 오픈한 호텔을 찾는다면 살펴보세요

C++에서 condition_variable의 사용법은?

_____
Q1: `std::condition_variable`이란 무엇인가요?
`std::condition_variable`은 스레드 간에 특정 조건이 만족될 때까지 대기하고, 조건이 변경되었음을 다른 스레드에 알리는 동기화 도구입니다. 일반적으로 뮤텍스(`std::mutex`)와 함께 사용됩니다.

---

Q2: `std::condition_variable`을 사용하려면 어떤 헤더를 포함해야 하나요?
```cpp
include
include
```

---

Q3: `std::condition_variable` 기본 사용법은 어떻게 되나요?
1. `std::mutex`와 `std::unique_lock`를 준비한다.
2. 조건이 만족하지 않으면 `wait()`를 호출해 대기한다.
3. 다른 스레드가 조건을 만족시킨 후, `notify_one()` 혹은 `notify_all()`을 호출해 대기 중인 스레드를 깨운다.

예:
```cpp
include
include
include

std::mutex mtx;
std::condition_variable cv;
std::queue dataQueue;

void producer() {
{
std::lock_guard lk(mtx);
dataQueue.push(42);
}
cv.notify_one(); // 데이터 생산 완료 알림
}

void consumer() {
std::unique_lock lk(mtx);
cv.wait(lk, []{ return !dataQueue.empty(); }); // 큐가 비어있으면 대기
int data = dataQueue.front();
dataQueue.pop();
// 락은 wait에서 반환 후 유지됨
}
```

---

Q4: `wait()` 메서드의 두 가지 형태 차이는 무엇인가요?
- `wait(std::unique_lock& lock)`
조건이 만족할 때까지 기다리며, 중간에 스레드가 깨어날 수 있어 조건 검사를 직접 해야 한다.
- `wait(std::unique_lock& lock, Predicate pred)`
pred가 `false`일 때만 기다리며, 스레드가 깨어났을 때 자동으로 pred를 평가해 필요시 다시 대기한다. 즉, 스퍼리어스 웨이크업(*spurious wakeup*) 문제를 자동 처리한다.

---

Q5: 스퍼리어스 웨이크업(spurious wakeup)이란 무엇인가요?
스레드가 의도하지 않은 이유로 `wait()`에서 깨어나는 현상입니다. 그래서 조건 검사를 반드시 반복해서 해야 하며, 이 때문에 `wait()` 호출 시 반드시 조건 검사를 동반하거나, pred형 오버로드를 사용하는 것이 안전합니다.

---

Q6: 언제 `notify_one()`과 `notify_all()`을 써야 하나요?
- `notify_one()`: 하나의 대기 중인 스레드를 깨움. 일반적인 경우 사용.
- `notify_all()`: 모든 대기 중인 스레드를 깨움. 여러 스레드가 동시에 조건 변화에 반응해야 할 때 사용.

---

Q7: `std::mutex` 대신 `std::unique_lock`가 필요한 이유는?
`condition_variable::wait()`는 락이 잠긴 상태에서 호출되어야 하며, 대기 중에는 락을 해제하고, 깨어나면 다시 락을 획득하는 동작을 합니다. 따라서 `unique_lock`만 락의 획득 및 해제를 제어하는 기능을 제공해 `wait()`와 호환됩니다. `lock_guard`는 락 해제를 컨트롤할 수 없기 때문에 적합하지 않습니다.
---

Q8: `condition_variable` 사용 시 주의사항은 무엇인가요?
- 조건을 반드시 락 획득 후 검사할 것.
- `wait()` 호출 시 항상 조건 검사와 함께 쓰거나 pred 버전을 사용해 스퍼리어스 웨이크업 대비.
- 조건이 변경된 후 반드시 `notify_one()` 혹은 `notify_all()`을 호출.
- `condition_variable` 객체는 복사가 불가능하여 참조 또는 포인터로 관리해야 함.

---

Q9: 여러 스레드가 같은 `condition_variable`에 대기해도 되나요?
네, 여러 스레드가 동일한 `condition_variable`에서 대기할 수 있습니다. 이 경우 `notify_one()`은 임의의 한 스레드를 깨우고, `notify_all()`은 모두 깨웁니다.

---

Q10: 간단한 생산자-소비자 예제 코드를 보여주세요.
```cpp
include
include
include
include
include

std::mutex mtx;
std::condition_variable cv;
std::queue dataQueue;
bool finished = false;

void producer() {
for (int i = 1; i <= 5; ++i) {
{
std::lock_guard lk(mtx);
dataQueue.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
{
std::lock_guard lk(mtx);
finished = true;
}
cv.notify_all();
}

void consumer() {
std::unique_lock lk(mtx);
while (!finished || !dataQueue.empty()) {
cv.wait(lk, [] { return finished || !dataQueue.empty(); });
while (!dataQueue.empty()) {
int data = dataQueue.front();
dataQueue.pop();
std::cout << "Consumed: " << data << std::endl;
}
}
}

int main() {
std::thread prod(producer);
std::thread cons(consumer);

prod.join();
cons.join();
return 0;
}
```

---

이상으로 `std::condition_variable`의 주요 개념과 사용법에 대해 FAQ 형식으로 설명드렸습니다.
C++에서 `condition_variable`은 스레드 간의 동기화를 위한 중요한 도구입니다.

이 객체는 특정 조건이 충족될 때까지 스레드를 대기시키고, 조건이 충족되면 대기 중인 스레드를 깨우는 기능을 제공합니다.

`condition_variable`은 주로 생산자-소비자 문제와 같은 상황에서 사용됩니다.

기본 개념 `condition_variable`은 두 가지 주요 작업을 지원합니다: 1. 대기 : 스레드가 특정 조건이 충족될 때까지 대기하도록 합니다.



2. 신호 : 조건이 충족되었음을 다른 스레드에 알립니다.

이러한 기능은 `std::mutex`와 함께 사용되어야 하며, `std::unique_lock`을 통해 mutex를 관리합니다.

사용법 1. 헤더 파일 포함 : `condition_variable`을 사용하기 위해서는 `` 헤더 파일을 포함해야 합니다.

```cpp include include include include ```

2. 변수 선언 : `condition_variable`, `mutex`, 그리고 조건을 나타내는 변수를 선언합니다.

```cpp std::condition_variable cv; std::mutex mtx; bool ready = false; // 조건 변수 ```

3. 대기하는 스레드 : 조건이 충족될 때까지 대기하는 스레드를 구현합니다.

`std::unique_lock`을 사용하여 mutex를 잠그고, `wait` 메서드를 호출하여 대기합니다.

```cpp void waitForWork() { std::unique_lock lock(mtx); cv.wait(lock, [] { return ready; }); // ready가 true가 될 때까지 대기 std::cout << "Work is done!" << std::endl; } ```

4. 신호를 보내는 스레드 : 조건이 충족되었을 때 `notify_one` 또는 `notify_all` 메서드를 호출하여 대기 중인 스레드를 깨웁니다.

```cpp void doWork() { { std::lock_guard lock(mtx); ready = true; // 조건을 충족 } cv.notify_one(); // 대기 중인 스레드 중 하나를 깨움 } ```

5. 스레드 실행 : 메인 함수에서 스레드를 생성하고 실행합니다.

```cpp int main() { std::thread worker(waitForWork); std::thread producer(doWork); worker.join(); producer.join(); return 0; } ``` 전체 코드 예제 아래는 `condition_variable`을 사용하는 전체 예제입니다.

```cpp include include include include std::condition_variable cv; std::mutex mtx; bool ready = false; void waitForWork() { std::unique_lock lock(mtx); cv.wait(lock, [] { return ready; }); // ready가 true가 될 때까지 대기 std::cout << "Work is done!" << std::endl; } void doWork() { { std::lock_guard lock(mtx); ready = true; // 조건을 충족 } cv.notify_one(); // 대기 중인 스레드 중 하나를 깨움 } int main() { std::thread worker(waitForWork); std::thread producer(doWork); worker.join(); producer.join(); return 0; } ``` 주의사항 - Mutex와 함께 사용 : `condition_variable`은 반드시 `mutex`와 함께 사용해야 합니다.

대기 중인 스레드는 mutex를 잠그고 있어야 하며, 조건이 충족되면 mutex를 해제하고 대기 상태에서 벗어납니다.

- 스레드 안전성 : `condition_variable`과 관련된 모든 데이터는 스레드 안전하게 접근해야 합니다.

이를 위해 mutex를 사용하여 데이터 접근을 보호해야 합니다.

- notify_all vs notify_one : `notify_one`은 대기 중인 스레드 중 하나만 깨우고, `notify_all`은 모든 대기 중인 스레드를 깨웁니다.

상황에 따라 적절한 방법을 선택해야 합니다.

결론 `condition_variable`은 C++에서 스레드 간의 동기화를 효과적으로 관리하는 데 매우 유용한 도구입니다.

이를 통해 복잡한 스레드 간의 상호작용을 간단하게 처리할 수 있으며, 생산자-소비자 문제와 같은 다양한 동기화 문제를 해결할 수 있습니다.

작성자: 김하은 [비회원] | 작성일자: 1년 전 2024-09-20 17:11:34
조회수: 121 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
내용이 부정확하다면 싫어요를 클릭해주세요.