-
생산자/소비자 패턴에서의 WaitForSingleObject()개발/C·C++ 2021. 9. 1. 18:36
윈도우에서 제공하는 이벤트 객체와 WaitForSingleObject()를 통해서 실행순서를 동기화할 수 있습니다. 이것이 중요한 이유는 소비자 스레드는 생산자 스레드에서 큐 자료구조에 데이터를 넣어줘야 그 데이터를 꺼내서 필요한 작업을 할 수 있는데, 대기 시간이 길어질 경우 소비자 스레드가 무한 대기하는 것은 효율적으로 CPU를 사용하는 것이 아니기 때문입니다. 대기하는 것에만 CPU 시간을 쓰는 것은 아깝습니다.
mutex m; queue<int32> q; HANDLE handle; void Producer() { while (true) { { unique_lock<mutex> lock(m); q.push(100); } this_thread::sleep_for(100ms); } } void Consumer() { while (true) { unique_lock<mutex> lock(m); if (false == q.empty()) { int32 data = q.front(); q.pop(); // lock 걸고 출력하는 게 좋은 건 아니지만 학습을 위해 cout << data << endl; } } }
이 코드를 실행하면 CPU 점유율이 일정하게 유지됩니다. 100ms 동안 아무 것도 안 하고 대기하느라 말이죠.
데이터가 없으면 소비자 스레드는 블록 상태로 들어가고 그동안 CPU는 다른 스레드에 제어권이 넘어가 있다가 데이터가 들어왔을 때 스레드를 깨워 할 일을 시키는 게 더 효율적입니다. 이를 위해 윈도우의 이벤트 객체와 WaitForSingleObject()를 사용할 수 있습니다. 이벤트 객체는 CreateEvent() 함수를 통해 만들 수 있습니다. 지금 상황에선 두 번째 인자에 false를 넣어 자동으로 상태가 변경되도록 하고, 초기값은 non-signaled로 하기 위해 false를 넣겠습니다.
기본 흐름은 생산자 스레드에 데이터가 들어왔을 때 SetEvent(handle)를 호출해 handle을 signaled로 바꿔줍니다. WaitForSingleObject()에 전달된 핸들이 non-signaled 일 때 해당 스레드가 블락됩니다. 핸들이 signaled로 바뀌었을 때 함수가 반환되기 때문에 실행 순서를 동기화할 수 있습니다. 여기에서 이벤트를 자동으로 설정했을 경우에는 WaitForSingleObject()가 반환되면서 상태가 non-signaled로 변경됩니다. 다른 소비자 스레드 스레드가 있을 경우 WaitForSingleObject()에서 블락될 겁니다. 수동일 경우에는 필요에 따라 ResetEvent(handle)을 호출해서 non-signaled로 변경해줍니다.
mutex m; queue<int32> q; HANDLE handle; void Producer() { while (true) { { unique_lock<mutex> lock(m); q.push(100); } // to signaled SetEvent(handle); this_thread::sleep_for(100ms); } } void Consumer() { while (true) { WaitForSingleObject(handle, INFINITE); unique_lock<mutex> lock(m); if (false == q.empty()) { int32 data = q.front(); q.pop(); // lock 걸고 출력하는 게 좋은 건 아니지만 학습을 위해 cout << data << endl; } } } int main() { handle = CreateEvent(nullptr/*보안속성*/, false/*bManualReset*/, false/*bInitialState*/, nullptr); thread t1(Producer); thread t2(Consumer); t1.join(); t2.join(); CloseHandle(handle); }
두 줄이 추가면서 CPU가 전보다 효율적으로 작동합니다.
'개발 > C·C++' 카테고리의 다른 글
스레드 함수를 람다식 안에서 사용할 때 주의할 점 (0) 2021.09.15 split() (0) 2021.09.11 sleep_for(), yield() (0) 2021.09.01 비주얼 스튜디오에서 다른 프로젝트의 파일 참조하기 (0) 2021.08.31 [Rvalue reference] Forcing Move Semantics(4) (0) 2021.08.26