Event 와 CriticalSection 을 이용한 동기화 문제
8개의 스레드를 돌려 Queue 에 들어간 데이터를 처리하는 클래스가 있습니다.
즉 8개의 스레드 각각은 Queue를 Pop() 하여 데이터를 처리하고, 이때 CriticalSection을 이용하여 Queue에 대한 동기화 처리를 해줍니다. (8개의 스레드가 동시에 접근할 가능성이 있으므로)
쉽게 하기 위해 이 스레드를 PopThread라고 합니다.
다른 1개의 스레드는 같은 Queue에 처리할 데이터를 Push()합니다. 물론 이때도 Push()할 때에는 CriticalSection으로 Queue에 대한 동기화를 해줍니다. (위의 8개의 스레드가 동시에 접근하고자 하므로)
이 스레드는 PushThread 라고 합시다.
이때 이 Push()하는 스레드가 처리할 데이터를 Push()했음을 8개의 Pop() 스레드에게 알리기 위해, 즉 PushThread가 데이터를 Push() 했음을 PopThread에게 알리기 위해 Queue에 데이터를 Push() 한 뒤 SetEvent() 를 날려 이를 알립니다.
그리고 PopThread에서는 WaitForSingleObject() 로 PushThread에서 SetEvent 될 이벤트를 무한정 기다립니다.
그런데 여기서 문제가 생깁니다.
PushThread 에서 SetEvent를 할때
Sync( m_CS ) { m_DataQueue.push_back( Data ); } SetEvent( m_ProcessDataEvent );
와 같이, 크리티컬 섹션 동기화는 오직 Queue에만 해주는 경우와 (논리적으로는 이게 맞죠. 크리티컬 섹션을 사용하는 목적 자체가 Queue에 대한 접근을 제어하기 위함이니까요)
(이 경우를 A 경우라고 합시다)
Sync( m_CS ) { m_DataQueue.push_back( Data ); SetEvent( m_ProcessDataEvent ); }
와 같이 크리티컬 섹션 동기화를 SetEvent()에도 같이 해주는 경우가 있습니다.
다만 이 경우는 논리적으로 약간 말이 안될수도 있는게, 크리티컬 섹션은 Queue에 대한 동기화 처리를 위한 것이지, m_ProcessDataEvent에 대한 동기화 처리를 위한 것은 아니기 때문이죠.
또한 SetEvent는 ThreadSafe 하다고 알고 있습니다.
(이 경우를 B 경우라고 합시다)
제 생각으로는 이 두 경우 결과가 같아야합니다. 왜냐하면 SetEvent는 ThreadSafe 하기 때문에 크리티컬 섹션으로 동기화 처리를 해주던 말던 어차피 동기화가 되기 때문이죠.
그런데 실제 테스트에서는 이 두 경우가 다르게 나옵니다.
A의 경우, 논리적으로는 문제가 없음에도 불구하고 SetEvent() 되는 이벤트를 기다리는 8개의 스레드가 무한 루프에 빠집니다. WaitForSingleObject() 에서 기다리는 Event가 Set되지 않았음을 뜻하죠( 이 부분은 로그를 통해 확인해본 사항입니다)
반면 B의 경우는 논리적으로 생각하기에는 약간 말이 안되지만, 실제로는 잘 동작합니다. SetEvent도 제대로 동작하고 WaitForSingleObject()역시 무한루프에 빠지지 않습니다.
제 짧은 지식으로는 문제가 없어 보이는 두 경우가 왜 다르게 나오는 걸까요?
혹시 Critical Section과 Event 동기화를 동시에 사용하는 경우에 주의해야 하는 사항이라도 있는 걸까요?
Event를 이용하는데 있어서 제가 모르는 버그라도 있는걸까요? ( 작업환경은 VS.2005 입니다 )
저랑 비슷한 상황을 겪어보셨거나, 답을 알고 계신 분은 한마디 조언 부탁드립니다. ㅠㅠ
PopThread쪽 코드는 어떻게 되나요?
PopThread쪽 코드는 어떻게 되나요?
PopThread쪽은..
WaitForSingleObject( m_ProcessDataEvent, INFINITE ); // 대충 이런식으로 이벤트를 기다립니다
Sync( m_CS )
{
// 아래와 같이 크리티컬 섹션으로 큐에 대한 동기화를 해줘서 데이터를 처리합니다.
p_Data = m_DataQueue.front();
m_DataQueue.pop();
ProcessData( p_Data );
}
// 데이터 처리가 끝나면 이벤트를 reset 시켜줍니다
ResetEvent( m_ProcessDataEvent );
대충 이런 형태입니다
gilgil.net
m_ProcessDataEvent 의 생성자에서 manualReset이 true인지 아니면 false인지 확인해 보세요.
그런데 push thread가 하나이고, pop thread가 여러개인가요?
www.gilgil.net
음..정확히는
m_ProcessEvent는 생성시에 CreateEvent( NULL, FALSE, FALSE, NULL) 로 기본인자가 주어지구요..
정확히는 pop thread( 즉 queue 데이터에 대한 worker thread) 가 8개이고, push thread는 따로 있는게 아니라 push 함수가 있어서 다른 스레드들이 해당 push함수를 호출하는 형식이라 딱 갯수가 정해진것이 아닙니다
bManualReset 값이..
bManualReset 값이 FALSE로 초기화 됬으니까 auto reset event를 생성하는게 되고..
그럼 ResetEvent가 안먹히게 된단 말씀이신가요?
autoReset이 됬기 때문에, push 쪽에서 SetEvent 하고 8개 쓰레드 중 하나가 WaitForSingleObject()로 이벤트를 받아오는 순간 자동으로 Reset되나요?
댓글 달기