[0415수업] Thread
1. 대화상자 기반의 새 프로젝트 생성
2. 버튼 추가, 캡션 수정(Button1 → Run Note Pad)
3. 버튼 더블클릭하여 ThreadDlg.cpp에 이벤트 처러기 생성
4. ThreadDlg.cpp에 노트패드만 스레드로 따로 돌릴 함수 생성
어제의 코드(WorkerThread)에서 복사해와 if(::ShellExecuteEx(&sei)) 문 수정
UINT ThreadWaitNotepad(LPVOID pParam)
{
TCHAR szWinPath[MAX_PATH];
// 윈도우즈 운영체제가 설치된 디렉토리 경로를 알아내는 함수
::GetWindowsDirectory(szWinPath, MAX_PATH);
// System 32 폴더의 경로는 GetSystemDirectory() API함수 사용
lstrcat(szWinPath, TEXT("\\notepad.exe")); //노트패드의 경로를 가리킴
SHELLEXECUTEINFO sei;
::ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.hwnd = NULL;
sei.lpFile = szWinPath;
sei.nShow = SW_SHOW;
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
sei.lpVerb = __TEXT("open");
sei.lpParameters = NULL;
if(::ShellExecuteEx(&sei))
{
HANDLE arhList[2];
arhList[0]=sei.hProcess;
arhList[1]=(HANDLE)g_ExitEvent;
DWORD dwResult = ::WaitForMultipleObjects(2, arhList, FALSE, INFINITE);
if(dwResult == WAIT_OBJECT_0)
{
AfxMessageBox(TEXT("메모장이 종료되었습니다"));
}
else if (dwResult == WAIT_OBJECT_0+1)
{
OutputDebugString(TEXT("예제 프로그램이 종료되었습니다"));
}
}
g_pThread = NULL;
return 0;
}
선언하지 않은 g_pThread 선언 필요
→ stdafx.cpp파일에
CWinThread* g_pThread = NULL;
CEnent g_ExitEvent;
선언.
매크로가 아닌 전역변수이기 때문에(코드이므로) stdafx.h파일이 아닌 stdafx.cpp에 선언한다
(둘 중에 어디에 넣어도 등록은 됨. 그러나 헤더파일에는 주로 매크로 선언..)
cf.)
변수의 접두사
g_(전역변수)
m_(해당필드의 멤버 변수)
stdafx.h에
<afxmt.h> 인클루드.
extern CWinThread* g_pThread;
extern CEvent g_ExitEvent; // 전역변수의 형식지정자 선언
WaitForMultipleObjects(): 핸들값을 감시함
(실행시킨 메모장의 종류여부를 감시함→hProcess핸들 감시를 통해서
→ 메모장 자체를 감시하는 것이 아니라 hProcess안에 들어있는 hInstance핸들 값의 변화를 감시하는 것)
스레드함수가 하고 있는 일: 메모장이 실행되어 있는지 여부를 계속해서 확인함. (메모장 자체는 스레드가 아님)
6. ThreadDlg.cpp의 OnBnClickedButton1이벤트 처리기에서 ThreadWaitNotepad()함수 호출
MFC의 스레드 함수 이용(접두사 Afx를 사용하는것은 MFC의 함수)
void CThreadDlg::OnBnClickedButton1()
{
if (g_pThread != NULL)
{
AfxMessageBox(TEXT("메모장 감시 스레드가 이미 실행 중입니다"));
}
g_pThread = AfxBeginThread(ThreadWaitNotepad, NULL);
if(g_pThread == NULL)
{
AfxMessageBox(TEXT("스레드 실행에 실패하였습니다"));
}
}
(보충설명)
AfxBeginThread(함수이름, NULL);
g_pThread에 리턴값을 받음
if(g_pThread == NULL) // 값을 리턴 받은 후에 실행
⇒ 메모장을 사용중에도 뒤쪽의 Thread프로세스가 정상 작동한다.
■ 스레드를 사용해야 하는경우
-어떤 데이터의 변화를 감시해야 할 때
-반복횟수가 너무 많거나 길고 반복횟수를 정확히 알지 못할때
■ 스레드를 쓸 때의 좋은점(장점)
일반함수의 경우 정지시키려면 전원차단 혹은 프로세스 강제종료 하지만 데이터(지역변수가 가지고 있던 값들)가 손실되는 위험이 따른다.
하지만 스레드를 사용하면, 외부함수에서 endthread하여 종료시킬 수 있다.(OS가 아니라 application쪽에서 종료할 수 있다)
스레드 중지
Thread의 어플리케이션 클래스에서 ExitInstance()함수 재정의
Thread.cpp에 ExitInstance()함수 코드 작성
int CThreadApp::ExitInstance()
{
g_ExitEvent.SetEvent();
::Sleep(10000);
return CWinApp::ExitInstance();
}
ExitInstance() : 종료하기 직전에 자동으로 실행되는 함수