2014년 10월 26일 일요일

MFC 스레드 다루기(리턴받기 잠재우기)

2017-01-18 추가...
조빱일때 내가 쓰레기를 써놨다니....ㅠㅠ AfxEndThread는 사용하면 안되는 함수다. 스레드 내부에서 스레드를 강제로 종료시킬 때 쓰는 함수로, 메모리 릭이 발생할 수 있다.
예를 들어...
UINT Calclt(LPVOID arg)
{
 CString s("aaa");
 AfxEndThread(0, TRUE);
}

CStrign객체가 정상 소멸되기 전(Stack에서 삭제)에 스레드가 끝나서 메모리릭이 발생할 수 있다. 절대 사용하지 말고, 그냥 return 0; 하면 된다. 이 글 보고 잘못 판단했을 분들에게 사과를....

그래서 다시 쓴다.

MFC에서 스레드를 사용하기 전에 알아두어야 할 함수는 AfxBeginThread, GetExitCodeThread, WaitForSingleObject 이렇게 있다. 이 함수에 대한 인자값은 MSDN을 참고하기 바라며, 간단한 사용법을 알아보자.

AfxBeginThread(CalcIt, (LPVOID)val);

첫 번째 인자는 전역함수인데, 클래스에 포함시킬 경우 정적함수로 선언해야되고, 아니면 전역함수로 선언해서 써야한다. CalcIt 함수의 리턴값과 파라미터는 아래와 같다.

UINT CalcIt(LPVOID arg);

꼭 위의 규칙을 따라줘야 한다.

일반적으로는 위와 같이 스레드를 시작하면 된다. 그러면 스레드가 시작되고, CalcIt함수의 역할이 끝나면 자동으로 스레드를 메모리에서 삭제한다..

하지만 스레드는 위와 같이 쓸 경우 AfxBeginThread를 하자마자 바로 리턴해버린다. Calclt함수에서 리턴을 받은 다음에 다음 동작을 해야한다면, 기다리거나 스레드 내부에서 SendMessage PostMessage를 호출하는 방법이 있다. 여기서는 기다리는 방법을 알아본다.

우선은 스레드가 메모리에서 자동 삭제되지 않도록 해야한다.

CWinThread* pThread = AfxBeginThread(CalcIt, (LPVOID)val, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
우선 스레드를 시작하지만 정지상태로 놓는다는 뜻이다.(CREATE_SUSPENDED)

그 다음
pThread->m_bAutoDelete = FALSE;
위에서 말했듯이, 스레드가 시작되고 자기 할일이 끝나면 바로 스레드는 종료된다. 하지만 스레드에서 정상처리가 되었는지 스레드 종료와 함께 받고 싶다면 자동삭제를 하면 안된다. 이 값을  FALSE로 해주면 스레드가 종료해도 메모리에서 삭제되지 않는다. 대신 사용자가 직접 스레드를 삭제 해줘야 한다.

다시 한번 말하지만 만약 스레드에서 받을 결과값이 없다면 굳이 FALSE로 하지 않아도 된다. 그대로 두면 알아서 삭제된다. 그렇다면 CREATE_SUSPENDED 로 시작할 필요도 없다. 바로 시작하면 된다.

다시 돌아가서...
pThread->ResumeThread();
이제 스레드를 시작한다.

그럼 리턴값을 받아야 한다.
::GetExitCodeThread(pThread->m_hThread, &returnvalue); //Get ExitCode from thread.

이 함수는 스레드로 부터 리턴값을 읽어온다. 여기에 포함되는 리턴값을 받기 위해선 위의 CalcIt 함수에서 리턴을 해줘야 한다. 스레드 내부에서 return 0; 등의 정상적인 리턴을 해주면 된다.

그리고 마지막으로 알아둬야 할 것이, WaitForSingleObject 함수이다.

역할은 Sleep과 동일하지만, 이 함수의 첫 번째 인자에 있는 스레드가 사용할 수 있게 될 때까지 기다리는 것이다.

Sleep은 스레드를 멈추는 역할밖에 못하지만, WaitForSingleObject는 특정 시간동안 이벤트를 감지할 수 있다.

WaitForSingleObject 의 두 번째 인자에는 밀리세컨드 단위의 시간이 들어가는데, INFINITE가 들어가면 스레드가 끝날때까지 기다리게 됩니다.

만약 구현이 된다면 아래와 같은 구조가 될 것이다.

  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.
delete pThread; //m_bAutoDelete 가 FALSE이므로 직접 삭제해줘야 한다.

위 내용은 기초적인 스레드 생성 방법이며, 위처럼 할 경우에 HANGING 현상으로 프로그램이 죽을 수 있다.(WaitForSingleObject때문에 죽음.)

HANGING으로 죽는 것을 방지하려면
MsgWaitForMultipleObjects를 이용하면서 메시지 펌핑을 하면 된다. 이 내용은 다른 글에 쓰겠다.

댓글 없음: