스레드 종료시점 알기위해서
::WaitForSingleObject(m_pMyThread->m_terminateEvent,INFINITE); 를 사용하면 Deadlock 발생가능성이 높다
쓰레드를 생성하고 쓰레드에서 처리된 결과 값을 얻기 위해 사용하는 WaitForSingleObject는 주로 아래와 같은 용도로 사용한다.
pThread = AfxBeginThread(ExportVVF, &arg1, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); //Make Thread with suspension
pThread->m_bAutoDelete = FALSE; //if this thread will be end, object will not destory this thread.
pThread->ResumeThread(); //Begin Thread
WaitForSingleObject(pThread->m_hThread, INFINITE);
::GetExitCodeThread(pThread->m_hThread, &returnvalue); //Get ExitCode from thread.
즉 쓰레드가 끝날 때까지 기다렸다가 결과 값을 얻는 것이다. 이 때 메인 쓰레드는 저기에 멈춰있다.
이때 새로 생성한 쓰레드 내부에서 SendMessage나 PostMessage를 이용하여 메인쓰레드로 메시지를 보낼 경우, DeadLock이 걸리게 된다. 원인은 다음과 같다.
Main Thread에서 WaitForSingleObject를 호출하여 Work Thread(생성한 쓰레드)의 실행 종료를 대기.
Work Thread(생성한 쓰레드)에서 SendMessage를 호출하여 Main Thread의 메세지 처리 완료를 대기.
위와 같은 구조로 인해 DeadLock이 발생된다. 서로 기다리고 있기 때문이다.
이것을 피하려면 2가지 방법이 존재할 수 있다.
1. WaitForSingleObject를 사용하지 않는다. 즉, 결과값을 얻지 않고 스레드를 이용하는 것이다. 하지만 이것은 상황에 따라 다를 수 있으며, 꼭 필요하다면 어쩔 수 없다.
2. WaitForSingleObject 대신에 MsgWaitForMultipleObject를 이용하여 다음과 같이 한다.
먼저 쓰레드를 생성하기 전에 Event를 생성하여 놓는다.
HANDLE m_hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
그리고 쓰레드가 종료되는 곳에서 SetEvent( m_hEvent );를 한다.
쓰레드 종료를 대기하는 곳에서 다음과 같이 한다.
while(TRUE){
if( m_hEvent == NULL )
break;
switch(MsgWaitForMultipleObjects(1, &(m_hEvent), FALSE, INFINITE, QS_ALLINPUT))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
// TIMEOUT 처리
case WAIT_OBJECT_0+1:
default:
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
위 코드는 쓰레드를 대기하면서 계속 PeekMessage를 통해 일명 메시지펌핑을 하고 있는 것이다. 위 코드는 메인쓰레드에 존재해야 한다.
즉, WaitForSingleObject를 이용하여 무한정 기다리는게 아니라, 중간중간에 메시지 처리를 해주는 것이다.
MsgWaitForMultipleObject의 마지막 인자가 쓰레드를 깨울 메시지의 종류이다. 무한정 대기하다가 SendMessage를 받으면, 아래 스위치문을 실행하게 되는 것이다.
MSDN에서는 가급적 WaitForSingleObject보다 MsgWaitForMultipleObjects를 사용하기를 권장하고 있다.
만약 Event를 사용하고 싶지 않다면, 이벤트 대신 스레드의 핸들을 사용할 수 있다.(두 번째 인자) 다만,
MFC일 때는 AfxBeginThread로 쓰레드를 생성하여 CWinThread 객체를 리턴 받았을 경우 m_bAutoDelete를 FALSE로 해줘야 한다.
만약 기본 값인 TRUE로 할 경우, WAIT_OBJECT_0 값이 들어오지 않고, WAIT_FAILED 값이 들어올 것이다.
스레드가 종료되면서 스레드 핸들이 자동으로 닫혔기 때문이다. 이 경우에는 WAIT_FAILED를 하나 더 구분하여 GetLastError()의 값이 ERROR_INVALID_HANDLE인지 비교하여 종료하면 되겠다.
'개발언어 > c++' 카테고리의 다른 글
클래스 템플릿 (0) | 2016.06.13 |
---|---|
함수 템플릿 (0) | 2016.06.13 |
RaiseException,예외이벤트 발생 (0) | 2016.06.12 |
RaiseException,예외이벤트 발생 (0) | 2016.06.12 |
Visual C++ 여러 창으로 열기 (2) | 2016.06.12 |