[C++] condition_variable 간략 설명

RisingJade의 개발기록·2022년 4월 10일
0

condition_variable

  • 유저레벨에서(같은 프로그램에서) 동기화가 필요할 경우 쓰인다.
  • 특정 조건을 만족하면 lock을 얻은 뒤 해당 쓰레드를 계속 실행시키고 아닐경우 다시 wait()을 발생시켜 대기하는 일을 할 때 주로 쓰인다.

    조건 변수를 대기하는 코드에서는 mutex를 사용해야 합니다. 조건 변수를 대기하는 함수를 호출하기 전에 호출 스레드에서 mutex를 잠가야 합니다. 호출된 함수가 반환될 때 mutex가 잠깁니다. 스레드가 조건이 true가 될 때까지 대기하는 동안에는 mutex가 잠기지 않습니다. 예기치 않은 결과가 없도록 조건 변수를 대기하는 각 스레드는 동일한 mutex 개체를 사용해야 합니다.

    condition_variable_any 형식의 개체는 모든 종류의 뮤텍스와 함께 사용할 수 있습니다. 사용되는 뮤텍스의 형식에서 try_lock 메서드를 제공할 필요가 없습니다. condition_variable 형식의 개체만 unique_lock 형식의 뮤텍스와 함께 사용할 수 있습니다. 이 형식의 개체는 condition_variable_any<unique_lock> 형식의 개체보다 빠를 수 있습니다.

    이벤트를 대기하려면 먼저 뮤텍스를 잠근 후 조건 변수에 대한 wait 메서드 중 하나를 호출합니다. 다른 스레드가 조건 변수를 알릴 때까지 wait에서 블록을 호출합니다.

    조건 변수를 대기 중인 스레드가 적절한 알림 없이 차단 해제될 때 의사 대기 모드 해제가 발생합니다. 이러한 의사 대기 모드 해제를 인식하기 위해 대기 함수에서 코드가 반환될 때 조건이 true가 될 때까지 대기하는 코드에서 해당 조건을 명시적으로 확인해야 합니다. 이는 보통 루프를 사용하여 수행됩니다. wait(unique_lock& lock, Predicate pred)를 사용하여 이 루프를 자동으로 수행할 수 있습니다.

    https://docs.microsoft.com/ko-kr/cpp/standard-library/condition-variable?view=msvc-170


간단한 예시

#include <iostream>
#include <thread>
#include <mutex>
#include <Windows.h>

mutex m;
queue<int32> q;

//커널 오브젝트: 커널에서 관리하는 객체 -> 같은 프로세스가 아닌 다른프로세스랑 동기화할때 유용하게 쓰인다.
	// spinlock같은 기법은 유저레벨에서 동기화가 일어나는것이지만
	//  이벤트를 이용해서 커널오브젝트까지 이용해서 하는것은 너무 빈번하게 일어나는경우 비용(리소스 사용량)이 너무 높아진다.
    //따라서 condition_variable을 써보자
//유저레벨 오브젝트다! not kernel object!
condition_variable cv;

void Push() {
	while (true)
	{
		//1) lock을 잡고
		//2) 공유 변수 값을 수정
		// 3) 락을 풀고
		//4) 조건변수 통해 다른 쓰레드에게 통지
		{//lock 범위용 블록 처리
			unique_lock<mutex> lock(m);
			q.push(100);
		}
		//::SetEvent(handle);
		cv.notify_one();
		//this_thread::sleep_for(10000ms);
	}
	
}

void Pop() {
	while (true)
	{
		//::WaitForSingleObject(handle, INFINITE);//signal이 올때까지 대기, 수면(InNFINITE-> 무한정대기) 
		//스케쥴의 대상이 되지않는다!!. 따라서 CPU점유률이 거의 0까지 떨어진다.
		// Non-signal event의 manualReset옵션을 false로 해두었으니 자동으로 signal->non-signal로 바뀜
		// 만약 매뉴얼의 세팅값이 TRUE이면 ::ResetEvent(handle)을 호출해서 강제로 리셋해야된다.
		unique_lock<mutex> lock(m);//유니크 락만 cv.wait을 가능케한다. 왜냐면 락을 중간에 풀고 다시 잡고해야되는데 유닠락만 가등한 기능이기때문
		cv.wait(lock, []() {return q.empty() == false; });
		//1) lock을 잡고
		//2) 조건확인
		// - 만족O => 빠져나와서 이어서 코드를 진행
		// - 만족X => 락을 풀어주고 대기 상태
		//if (q.empty() == false)
		{
			int32 data = q.front();
			q.pop();
			cout << q.size() << endl;

		}
		
	}
}

int main()
{
	thread t1(Push);
	thread t2(Pop);

	t1.join();
	t2.join();

}
profile
언제나 감사하며 살자!

0개의 댓글