2017. 2. 17. 10:59 MFC/기초
MFC) Work Thread / UI(Usesr Interface) Thread 사용법 총 정리
MFC에서의 멀티쓰레드(Multithread)
MFC에서의 Multithread
OS는 구분하지 않지만 MFC는 사용자 편의를 위하여 두 가지 형태로 지원한다.
1. Worker thread
2. User Interface thread
1. Work Thread
- ::AfxBeginThread() 함수를 이용
[설명]
메시지 루프를 포함하지 않고, 스레드 작업 중 입력을 허가하지 않는 스레드 형식
=> 스레드 작업 중 별도의 입력이 필요없을 때 사용
[사용법]
(스레드 시작)
1. CWinThread *pWorkThread = AfxBeginThread(ThreadFunc, &pParam);
=>ThreadFunc가 전역함수인 경우 && Param값을 생성하여 넘겨줄 경우
2. CWinThread *pWorkThread = AfxBeginThread(ThreadFunc, this);
=> ThreadFunc가 전역함수인 경우 && Param으로 이 스레드를 시작하는 클래스를 넘겨줄 경우
3. CWinThread *pWorkThread = AfxBeginThread(ThisClass::ThreadFunc, &pParam);
=> ThreadFunc이 해당 클래스의 멤버함수인 경우 && Param값을 생성하여 넘겨줄 경우
4. CWinThread *pWorkThread = AfxBeginThread(ThisClass::ThreadFunc, this);
=> ThreadFunc이 해당 클래스의 멤버함수인 경우 && Param으로 이 스레드를 해당 클래스를 넘겨줄 경우
(스레드 일시중지) - 주 Process에서 해야하며, 스레드 자신이 호출 불가.
pWorkThread->SuspendThread();
(스레드 재개) - 주 Process에서 해야하며, 스레드 자신이 호출 불가.
pWorkThread->ResumeThread();
(스레드 동작 검사)
DWORD dwContextID; // 스레드 상태가 저장될 변수
GetExitCodeThread(pWorkThread->m_hThread, &dwContextID);
(Sleep())
ex) Sleep(2000);
- 제어권을 다른 Process에 넘겨 주게 된다.
ex) Sleep(0);
- 우선 순위가 높거나 같은 Process에 넘겨 주고 우선 순위가 높거나
같은 Process가 없을 경우에는 아무 일도 생기지 않음.
(스레드 종료)
1. TreminateThread(pWorkThread->m_hThread, 0);
=> 스레드를 강제종료한다. 메모리 릭이 발생할 수 있으므로 추천하지 않음.
2. WaitForSingleObject(); - 무한
if ( ::WaitForSingleObject(pThread->m_hThread, INFINITE) )
{
// 이곳은쓰레드가확실히종료된상태임
}
=> INFINITE 구문을 사용하여 스레드의 확실한 종료를 기다린다.
3. WaitForSingleObject(); - 시간지정
result = ::WaitForSingleObject(pThread->m_hThread, 1000); // 1초기다림
if ( result == WAIT_OBJECT_0 )
{
// 이곳은쓰레드가확실히종료된상태임
}
else if ( result == WAIT_TIMEOUT )
{
// 1초가지나도쓰레드가종료되지않은상태
}
=>시간 지정을 하여 스레드 종료 시 먹통 예방.
(스레드 실행중인지 검사)
if ( ::WaitForSingleObject(pThread->m_hThread, 0 ) == WAIT_TIMEOUT )
{
// pThread 실행중
}
else
{
// pThread가실행중이아님
}
2. UI Thread
[설명]
메시지 루프를 포함하여 스레드 작업 중 사용자의 입력을 메시지 형태로 받아 처리하는 스레드 형식
=> 스레드 작업 중 별도의 입력이 필요할 때 사용
User Interface Thread
User interface thread는 그 자체로 윈도우와 메시지 루프를 가지고 있다.
class CUIThread : public CWinThread
{
DECLARE_DYNCREATE(CUIThread)
public:
virtual BOOL InitInstance();
};
이 User interface thread는 독자의 윈도우도 가질 수 있다. 일반적으로 전용 Dialog를 띄워
Thread를 처리하는 경우가 많으므로 이 User Dialog를 CMyDialog라고 이름 지었다고 가정하면
IMPLEMENT_DYNCREATE(CUIThread, CWinThread)
BOOL CUIThread::InitInstance()
{
m_pMainWnd = new CMyDialog;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
와 같이 CMyDialog를 Thread로 띄울 수 있다. 그 다음
CWinThread *pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
와 같이 하면 MFC가 알아서 CUIThread를 생성해서 그 포인터를 pThread에 넘겨 준다.
아래 예제에는 CMyDialog를 띄우고 주 Process는 사용자의
입력을 기다린다. Dialog의 Design 및 생성은 별도로 이야기하지 않는다. 아래 예제를 사용하기 위해서는
CMyDialog를 만들고 ID를 IDD_MYDIALOG라고 가정하면 CMyDialog의 생성자에 다음과 같이 추가해야 제대로 동작한다.
CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/)
: CDialog(CMyDialog::IDD, pParent)
{
Create(IDD_MYDIALOG, NULL);
}
이제 완전히 별도로 동작하는(Thread로 동작하는) 윈도우를 하나 가지는 것이다. 만약 이것을 Dialog가 아닌
FrameWnd라고 해도 거의 똑같다. 다만 위에서도 언급했듯이 Thread를 이용할 때는 Dialog가 더 일반적일 것이다.
Worker thread에 비해 훨씬 더 많은 기능을 하는 것을 알게 되었을 것이다.
나머지 것들은 위의 Worker Thread에 있는 내용과 동일하다.
만약 여러 개의 CUIThread 를 여러 개 만들려고 한다면
CWinThread *pThread[5];
for ( int i = 0; i < 5; i++ )
m_pThread[i] = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
와 같이 하면 5개의 Thread가 생성된다.
Program Execution Priority(프로그램 실행 우선순위)
Thread는 0~31까지의 priority를 가질 수 있다.
프로그램의 priority는
BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
함수를 이용해서 조정할 수 있다. 첫 번째 인자 hProcess는 ::AfxGetInstanceHandle()로 얻으면 된다.
dwPriorityClass
Execution Priority
Description
DLE_PRIORITY_CLASS
CPU가 IDLE일 때만 사용 가능
ORMAL_PRIORITY_CLASS
보통
IGH_PRIORITY_CLASS
높은 우선 순위
EALTIME_PRIORITY_CLASS
최상위의 우선순위
Thread Execution Priority(쓰레드 실행 우선순위)
::AfxBeginThread() 함수의 nPriority를 통해서 설정하거나 CWinThread::SetThreadPriority 를 사용해 설정할 수 있다.
BOOL SetThreadPriority(HANDLE hThread, int nPriority);
nPriority
Execution Priority
Description
HREAD_PRIORITY_IDLE
REALTIME_PRIORITY_CLASS의 경우 16, 그 외에는 1
HREAD_PRIORITY_LOWEST
프로세스의 우선순위보다 2단계 낮은 우선순위를 가진다
HREAD_PRIORITY_BELOW_NORMAL
프로세스의 우선순위보다 1단계 낮은 우선순위를 가진다
HREAD_PRIORITY_NORMAL
프로세스의 우선순위가 같은 우선순위
HREAD_PRIORITY_ABOVE_NORMAL
프로세스의 우선순위보다 1단계 높은 우선순위를 가진다
HREAD_PRIORITY_HIGHEST
프로세스의 우선순위보다 2단계 높은 우선순위를 가진다
HREAD_PRIORITY_CRITICAL
REALTIME_PRIORITY_CLAS의 경우 31 그 외에는 16
프로그래머가 우선순위를 조정해도 Windows Scheduler가 상황에 맞게 조정하기 때문에 우선순위는
생각하고 조금 다를 수 있다.
Thread & Memory
각 Thread의 지역 변수는 모두 별도로 Stack을 만들고 Local Variable들을 관리하기 때문에 위의
CWinThread *pThread[5];
for ( int i = 0; i < 5; i++ )
m_pThread[i] = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
와 같은 경우에도 각 Thread가 다른 Thread를 침범하는 일은 없다.
이 쯤에서 끝났나 싶겠지만… 아직 갈 길이 멀다.
Critical section, Mutex, Semaphore 같은 것들은 다음에…
Multithreading synchronization(멀티쓰레드의 동기화) => http://blog.naver.com/xtelite/50023359879
프로그램
Worker thread
#include "stdafx.h"
#include "console.h"
#include <iostream>
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
UINT ThreadPrintNum(LPVOID pParam);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
return 1;
}
static BOOL bContinue = TRUE;
CWinThread *pThread = ::AfxBeginThread(ThreadPrintNum, &bContinue);
int count = 0;
while ( count < 1000 )
{
count++;
}
Sleep(1000);
pThread->SuspendThread();
cout << "Thread suspended. Waiting for 2 seconds" << endl;
Sleep(2000);
cout << "Thread resumed" << endl;
pThread->ResumeThread();
cout << "Quit thread" << endl;
bContinue = FALSE;
Sleep(100);
return nRetCode;
}
// 쓰레드함수
UINT ThreadPrintNum(LPVOID pParam)
{
BOOL *pbContinue = (BOOL *)pParam;
int count = 0;
while ( *pbContinue )
{
count++;
}
cout << "Exit thread" << endl;
return 0;
}
User interface thread
#include "stdafx.h"
#include "console.h"
#include "MyDialog.h"
#include <cstdlib>
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
class CUIThread : public CWinThread
{
DECLARE_DYNCREATE(CUIThread)
public:
virtual BOOL InitInstance();
};
IMPLEMENT_DYNCREATE(CUIThread, CWinThread)
BOOL CUIThread::InitInstance()
{
m_pMainWnd = new CMyDialog;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
return 1;
}
CWinThread *pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
system("pause");
return nRetCode;
}
출처 : "http://system.tistory.com/entry/멀티-쓰레딩"
'MFC > 기초' 카테고리의 다른 글
MFC) 여러 컨트롤(버튼 등) 이벤트 동시 처리 (0) | 2017.02.28 |
---|---|
MFC) ToolBar(툴바) 기본 사용법 및 팁 (0) | 2017.02.27 |
MFC) CString/문자열 함수(탐색 및 추출)/Find, ReverseFind, Left, Right, Mid (0) | 2017.02.09 |
MFC) CTreeCtrl 사용법(퍼옴) (0) | 2017.01.11 |
MFC) CListBox 사용법 (0) | 2017.01.10 |