본문 바로가기
개발 (언어)/C++

[MultiThread] Event, Condition Variable

by 진현개발일기 2024. 4. 8.

■ 스핀락, 일반 Lock, 이벤트


· 앞서 배운 예제로 Spinlock은 기내 화장실 앞에서 죽치고 쭈욱 기다리는 것이고, 일반 Lock은 내 자리에 돌아갔다가 다시 화장실이 빌 것 같을 때 자리가 있는지 확인하러 오는 방식이다.


· 이벤트 방식은 승무원에게 화장실이 비게 되면 알려달라고 부탁한 뒤 우리는 자리로 가서 대기하는 것이다.

 

■ 사용법

(1) #include <windows.h> 헤더를 추가한다.


(2) HANDLE 타입의 변수를 선언한다. 
- 여태까지 변수 선언을 하면 유저레벨에서 관리를 하던 변수들이었지만
- Handle은 운영체제가 관리하는 오브젝트를 만들어주게끔 요청할 수 있는 커널 오브젝트(Kernel Object)이다.

HANDLE 타입의 변수

커널 오브젝트 (Kernel Object) 구성 요소
모든 커널 오브젝트는 사용되어지고 있는 개수 (Usage Count) 상태 (Signal/Non-Signal)를 갖고있다.

 

(3) 아래와 같이 이벤트 변수를 생성해준다.


(4) 트리거를 작동시키고 (Signal On) 싶은 곳에 ::SetEvent(hEvent); 함수를 실행시키고
(5) 트리거가 작동될때까지 기다리게 하고싶은 곳에선 ::WaitForSingleObject 함수를 호출해놓는다.


(+ 수동리셋을 TRUE로 세팅해놓았으면 ::ResetEvent함수로 Singal을 리셋시킬 수 있다.)

(6) 이벤트를 다 사용하고나서는
::CloseHandle(hEvent); 와 같이 이벤트를 종료해줘야 한다.

 


■ 조건 변수 (Condition Variable)

C++ 11으로 넘어오면서 조건 변수가 생기게 되었다. 이로 인해 이벤트를 사용할 일이 크게 없을 것이다.
조건 변수는 커널오브젝트가 아니라 유저레벨 오브젝트이다.

 

[이벤트의 한계 및 조건변수의 이점]

(1) 위 Producer, Consumer예시에서 Thread 1이 Proudcer이고 Thread2, 3이 Consumer라고 할 때

 

(2) Thread1이 Signal On을 하면 Thread2, 3이 작동하게 될 것이다.

 

(3) 하지만 Thread2가 이미 실행해버려서 queue가 비어진 상태라면, 굳이 Thread3이 queue가 비어있는지 체크할 필요가 없는 것이다.

(4) 이를 한 번에 하게 해주는 것이 조건 변수이다. 그러므로 만약 스레드가 필요한 상황이 되었을 때 딱 필요한 개수만큼 깨워주는 변수이다.

 

[사용법]

(1) condition_variable cv; 와 같이 변수 선언

(2) 스레드가 일을 해야할 시점을 알려줄 때 cv.notify_one(); 함수를 호출
- notify_all을 하면 모든 스레드를 깨움

 

[왜 Lock을 먼저 잡고 notify를 하는 것인가?]
실행 원리는 이와 같다.

1. Lock 잡음

2. 공유 변수 값 수정

3. Lock을 풀고 <-이때 Lock을 푼다는 것이 중요

4. Condition Variable (CV)를 통해 통지

 

 

(3) 스레드가 일을 하는 함수 쪽에서

 · 락을 먼저 건다 

 · cv.wait()함수를 통해 특정 조건에서만 일을 하도록 알려준다.

 · 이때 형식은 cv.wait(lock, []() { return q.empty() == false; });와 같이 첫번째 파라미터로 Lock을, 두번째 파라미터로 원하는 조건을 람다로 넘겨준다

[근데 왜 Lock을 먼저 잡고 Condition Wait을 하나?]
그 이유는 실행 원리 때문이다.

1. Lock을 잡으려고 시도한다 (이미 잡혀있으면 Skip한다)

2. 조건 확인
- 만족 O -> 바로 빠져 나와서 이어서 코드 진행 (이때까지도  Lock이 잡혀있다)
- 만족 X -> Lock을 풀어주고 대기 상태로 전환  (Lock을 풀어준다는 것이 굉장히 중요함)

* 그래서 lock_guard가 아니라 중간에 락을 풀었다 걸었다 해줄 수 있는 unique_lock을 사용함

 

728x90