PROGRAMMING/MFC(C++)

[0415수업] Thread

마마필로 2011. 4. 15. 01:04

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() : 종료하기 직전에 자동으로 실행되는 함수