■ 스핀락, 일반 Lock, 이벤트
· 앞서 배운 예제로 Spinlock은 기내 화장실 앞에서 죽치고 쭈욱 기다리는 것이고, 일반 Lock은 내 자리에 돌아갔다가 다시 화장실이 빌 것 같을 때 자리가 있는지 확인하러 오는 방식이다.
· 이벤트 방식은 승무원에게 화장실이 비게 되면 알려달라고 부탁한 뒤 우리는 자리로 가서 대기하는 것이다.
■ 사용법
(1) #include <windows.h> 헤더를 추가한다.
(2) HANDLE 타입의 변수를 선언한다.
- 여태까지 변수 선언을 하면 유저레벨에서 관리를 하던 변수들이었지만
- Handle은 운영체제가 관리하는 오브젝트를 만들어주게끔 요청할 수 있는 커널 오브젝트(Kernel Object)이다.
커널 오브젝트 (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을 사용함 |
'개발 (언어) > C++' 카테고리의 다른 글
[MultiThread] Mutex, Spinlock, DeadLock (0) | 2024.04.08 |
---|---|
[MultiThread] 공유 자원, Atomic (0) | 2024.04.07 |
[MultiThread] 캐시 및 CPU 명령어 파이프라인 (1) | 2024.04.07 |
[MultiThread] 쓰레드 생성 (1) | 2024.04.07 |
[Modern C++] weak_ptr, unique_ptr (0) | 2024.03.31 |