조건 변수
조건 변수의 개념
조건 변수는 스레드 간 통신을 위한 동기화 기법으로, 특정 조건이 만족될 때까지 하나 이상의 스레드를 대기 상태로 만들고, 조건이 만족되면 대기 중인 스레드 중 하나 또는 모두를 깨우는 메커니즘을 제공합니다.
C++에서는 <condition_variable> 헤더에 정의된 std::condition_variable
클래스를 사용하여 조건 변수를 구현합니다.
기본 사용법
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker_thread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Worker thread is processing data\n";
}
void main_thread() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
int main() {
std::thread worker(worker_thread);
std::thread main(main_thread);
worker.join();
main.join();
return 0;
}
이 예제에서 worker_thread
는 ready
변수가 true
가 될 때까지 대기합니다. main_thread
에서 ready
를 true
로 설정하고 notify_one()
을 호출하여 대기 중인 스레드를 깨웁니다.
wait, notify_one, notify_all
wait()
: 조건 변수가 통지받을 때까지 현재 스레드를 블록합니다.notify_one()
: 대기 중인 스레드 중 하나를 깨웁니다.notify_all()
: 대기 중인 모든 스레드를 깨웁니다.
조건 변수와 술어 (Predicate)
조건 변수를 사용할 때는 항상 술어(조건)를 함께 사용하는 것이 좋습니다.
이는 허위 각성(spurious wakeup) 문제를 방지합니다.
cv.wait(lock, []{ return condition; });
이 방식은 다음과 동일합니다.
while (!condition) {
cv.wait(lock);
}
생산자-소비자 패턴 구현
조건 변수는 생산자-소비자 패턴을 구현할 때 매우 유용합니다.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
void producer() {
for (int i = 0; i < 10; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !data_queue.empty(); });
int data = data_queue.front();
data_queue.pop();
std::cout << "Consumed: " << data << std::endl;
if (data == 9) break;
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.join();
return 0;
}
타임아웃 처리
wait_for()
및 wait_until()
함수를 사용하여 조건 변수 대기에 타임아웃을 설정할 수 있습니다.
if (cv.wait_for(lock, std::chrono::seconds(5), []{ return ready; })) {
// 조건이 만족됨
} else {
// 타임아웃 발생
}
연습 문제
- 다중 생산자-다중 소비자 큐를 구현하세요. 여러 생산자 스레드와 여러 소비자 스레드가 동시에 작업할 수 있어야 합니다.
- 조건 변수를 사용하여 간단한 작업 스케줄러를 구현하세요. 작업을 추가하고 실행할 수 있어야 합니다.
- 세마포어를 조건 변수를 사용하여 구현하세요.
참고자료
- C++ Concurrency in Action (2nd Edition) by Anthony Williams
- The Art of Multiprocessor Programming by Maurice Herlihy and Nir Shavit
- C++ 표준 문서의 조건 변수 관련 섹션
- "Operating Systems : Three Easy Pieces" by Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau (특히 동기화 관련 장)
- CppCon 발표 영상들 - 동기화와 조건 변수 관련 세션들