쓰레드에 대해 알아보기 전에 타이머와 쓰레드의 차이를 간단히 설명하겠습니다.
저번 글에 쓴 것처럼 타이머의 경우 보통 간단한 연산과 대략적인 시간 간격일 경우 사용한다 했습니다.
타이머의 경우 윈도우 메세지로 ID를 판별하여 실행 되는데, 이 메세지가 정확한 시간 간격마다 오는게 아니라 비슷하게 옵니다.
만약 개발자가 1초마다 정확히 실행되는 프로그램을 만들고 싶으면, 함수 실행 시간을 고려해서 Sleep을 해야하는데, 타이머 메시지가 정확히 1초마다 날아오지 않는 것도 있지만, 내부에서 Sleep을 하면 대화상자가 멈추게 됩니다.
예시로, 저번 예제에서 아래 Sleep(1000); 구문을 추가합니다.
Sleep 인자는 밀리세컨드(1000msec = 1sec)이여서 1초 대기한다는 의미입니다.
CString sTxt; switch (nIDEvent) { sTxt.Format(_T("%d"), m_iCount++); m_staticDisp.SetWindowText(sTxt); Sleep(1000); break; }
실행을 하고 대화 상자를 드래그 해서 이동하면 Sleep구문이 실행될 때 대화상자가 멈추게 됩니다.
어찌 됐든 타이머는 생성과 삭제가 편리하지만 약간의 불편함이 있습니다.
이제 쓰레드의 생성, 중지, 재시작, 종료에 대해 알아보겠습니다.
우선 대화 상자에 버튼을 4개와 표시를 위한 스태틱 컨트롤 1개를 만듭니다.
CWinThread *m_pThread; CStatic m_staticDisp;
쓰레드 변수의 관리를 위해 OnInitDialog에 아래와 같이 쓰레드 변수를 초기화합니다.
m_pThread = NULL;
리소스 뷰에서 생성 버튼을 더블 클릭하여 쓰레드 생성 구문을 입력합니다.
if (NULL == m_pThread) { m_pThread = ::AfxBeginThread(TestThread, this); if (NULL == m_pThread) { ::AfxMessageBox(_T("AfxBeginThread Fail")); } }
쓰레드 생성을 하셨으면 무엇을 할지 만들어야합니다.
AfxBeginThread 인자 중 TestThread가 아직 만들지 않은 함수인데, 헤더파일과, cpp파일에 아래와 같이 입력합니다.
// .h static UINT TestThread(LPVOID lpVoid);
// .cpp UINT CBasicThreadUseDlg::TestThread(LPVOID lpVoid) { CBasicThreadUseDlg *pDlg = (CBasicThreadUseDlg *)lpVoid; int iNumber = 0; CString sTxt; while(1) { sTxt.Format(_T("%d"), iNumber++); pDlg->m_staticDisp.SetWindowText(sTxt); Sleep(1000); } return 0; }
이상태로 빌드 후 실행 하시면 아래와 같이 실행됩니다. 아래 그림을 보시면 Sleep이 들어갔지만 아까와 다르게 대화상자 이동이 잘 되는 것을 보실 수 있습니다.
생성된 쓰레드를 실행하다 중지하는 방법은 아래와 같습니다.
리소스 뷰에서 중지 버튼을 더블 클릭하여 아래와 같이 입력합니다.
::ResumeThread(m_pThread->m_hThread);
실행중인 쓰레드를 종료하는 방법은 아래와 같습니다.
리소스 뷰에서 종료 버튼을 더블 클릭하여 아래와 같이 입력합니다.
if (::TerminateThread(m_pThread->m_hThread, 1)) { ::CloseHandle(m_pThread->m_hThread); m_pThread = NULL; }
위 방식은 쓰레드의 정말 기본적인 사용법이지 정석은 아닙니다.
예를 들어서 쓰레드 실행 후 중지 버튼을 10번정도 누르면, 재시작 버튼을 1번만 누르면 재시작이 되지 않습니다.
똑같이 10번은 눌러야 쓰레드가 다시 재시작을 합니다.
그리고 TerminateThread는 강제 종료라고 보시면 됩니다. 별로 좋은 방식은 아니에요.
하지만 우선 하나씩 익혀가면서 점점 개념을 확장하는 방식으로 공부하셔도 좋습니다.