작성
·
35
·
수정됨
0
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <windows.h>
mutex m;
queue<int32> q;
HANDLE handle;
void Producer() {
while (true) {
{
unique_lock<mutex> lock(m);
q.push(100);
}
::SetEvent(handle);
std::this_thread::sleep_for(100ms);
}
}
void Consumer() {
while (true) {
::WaitForSingleObject(handle, INFINITE);
::ResetEvent(handle);
unique_lock<mutex> lock(m);
if (!q.empty()) {
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main() {
handle = ::CreateEvent(NULL, TRUE, FALSE, NULL);
std::thread t1(Producer);
std::thread t2(Consumer);
t1.join();
t2.join();
::CloseHandle(handle);
return 0;
}
에서 WaitForSingleObject이후에 signal이 auto가 아니라면 ResetEvent를 따로 적어주어야 한다 하셨는데, ResetEvent가 호출되기 전에 다른 쓰레드에 있는 SetEvent가 호출되면 어떻게 되는건가요?
답변 2
1
안녕하세요, 인프런 AI 인턴입니다. 박건우님.
질문해주신 내용은 멀티스레드 환경에서 이벤트 객체의 사용과 관련된 흥미로운 문제입니다. SetEvent
, WaitForSingleObject
, 그리고 ResetEvent
는 윈도우즈에서 스레드 간의 동기화에 사용되는 함수들입니다.
SetEvent: 이벤트 객체의 상태를 “신호 상태”로 설정합니다. 실제로 이벤트가 발생했다는 신호를 다른 스레드에 알리기 위해 사용됩니다.
WaitForSingleObject: 이벤트 객체의 상태가 “신호 상태”로 될 때까지 대기합니다. 신호 상태가 될 경우, 계속 실행이 가능하게 됩니다.
ResetEvent: 수동 이벤트인지 자동 이벤트인지에 따라 이벤트 객체의 상태를 “비신호 상태”로 리셋할 수 있습니다. 자동 이벤트라면 WaitForSingleObject가 호출된 뒤 자동으로 리셋됩니다. 그러나 수동 이벤트라면 사용자가 명시적으로 ResetEvent를 호출해서 리셋해야 합니다.
질문에서 언급하신 상황은 “SetEvent가 호출되기 전에 ResetEvent가 호출되면 어떻게 되는가?” 라는 것입니다. 예를 들어:
SetEvent 이후에 WaitForSingleObject 가 호출되고, 그 다음 ResetEvent 를 호출하면 비신호 상태로 변경되어 더 이상 다른 스레드가 해당 이벤트를 기다릴 수 없게 됩니다.
그러나 ResetEvent가 호출되기 전에 SetEvent가 호출되면, 이벤트는 신호 상태에 있으며 다른 스레드들이 이 이벤트를 기다릴 때 즉시 진행할 수 있게 됩니다. 이후에 ResetEvent가 호출되면 그 이벤트는 다시 비신호 상태가 됩니다. 이 경우 수동 리셋을 이용하므로, 정확한 동기화를 위해 이 과정에서 이벤트 발생 순서를 주의 깊게 관리해야 합니다.
이상과 같은 상황은 레이스 컨디션(race condition)이 발생할 수 있으므로 프로그램의 동작을 예측하기가 어려울 수 있습니다. 따라서 이벤트 객체를 사용할 때는 어느 점에서 ResetEvent를 호출할지를 신중히 계획하는 것이 중요합니다.
해당 질문과 유사한 논의를 다룬 링크를 참고하면 도움이 될 것입니다:
- Event에 관한 질문입니다.
위 링크에서는 여러 스레드가 같이 작업할 때 이벤트 상태가 어떻게 변할 수 있는지를 설명하고 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
0
Producer-Consumer는 1개 쓰레드씩 배치하는게 일반적이고,
단순한 이벤트 방식의 동기화 예제에서도 그 외 부분은 고려하지 않는데요.
Consumer를 여러개 한다면 상황은 훨씬 더 복잡해지고,
사실 그럴 때는 이 방식을 사용하지 않는게 정답입니다.
궁금하신 부분은 사실 저도 테스트 해보진 않아서 100% 확실하진 않지만
기본적으로 이벤트는 일종의 커널에서 관리하는 bool 처럼 생각하시면 됩니다.
따라서 다른 쓰레드에서 SetEvent를 했으면 Reset을 안 해도 WaitFor~에서 또 빠져 나오겠죠.
정말 궁금하시다면 이런 부분은 쓰레드를 늘려서 실험을 해보시거나,
MSDN이나 stackoverflow에 [SetEvent + threadsafe] 키워드르 자료 조사를 해보세요
그럼 WaitForSingleObject -> SetEvent -> ResetEvent순으로 코드가 실행되면 while문을 다시 돌아서 WaitForSingleObject부분을 실행해야 할 때, q에 데이터가 있어도 non-signal 상태라서 쓰레드가 커널 모드에서 대기하는건가요?