MFC 기반의 Multi-threading
근 20년간 MFC를 사용하고 있지 않다가 부득이하게 성능 테스트를 해볼 DLL을 타 부서에서 전달받았는데 C++로 되어 있고 사용자 인터페이스를 함께 작업해야해서 다시 한번 정리하는 의미로 글을 남긴다.
1. MFC에서의 기본 스레딩 개념
MFC에서는 CWinThread 클래스를 사용하여 스레드를 관리할 수 있다. 이 클래스는 윈도우 애플리케이션의 스레드와 관련된 여러 기능을 제공한다. Windows API의 CreateThread와 BeginThread와 같은 함수도 사용 가능하지만, MFC의 CWinThread 클래스를 사용하는 것이 보다 편리하다.
2. MFC에서 스레드 생성 및 관리
(1) 스레드 클래스 만들기
스레드를 만들려면 CWinThread를 상속한 클래스를 작성한다.
class CMyThread : public CWinThread
{
DECLARE_DYNCREATE(CMyThread)
public:
CMyThread() {}
virtual ~CMyThread() {}
virtual BOOL InitInstance() override;
virtual int ExitInstance() override;
virtual int Run();
};
IMPLEMENT_DYNCREATE(CMyThread, CWinThread)
BOOL CMyThread::InitInstance()
{
// 스레드 초기화
AfxMessageBox(_T("Thread started"));
return TRUE;
}
int CMyThread::ExitInstance()
{
// 스레드 종료시 정리 작업
AfxMessageBox(_T("Thread exited"));
return CWinThread::ExitInstance();
}
UINT CMyThread::Run()
{
// 여기에 스레드가 수행할 작업을 정의
for (int i = 0; i < 5; ++i)
{
AfxMessageBox(CString(_T("Thread is running")));
Sleep(1000); // 1초 대기
}
return 0;
}
(2) 스레드 시작하기
스레드를 시작하려면 AfxBeginThread 함수를 사용한다. 이 함수는 스레드를 생성하고 실행을 시작합니다.
void CMainFrame::StartThread()
{
CMyThread* pThread = (CMyThread*)AfxBeginThread(
RUNTIME_CLASS(CMyThread), // 스레드 클래스
THREAD_PRIORITY_NORMAL, // 스레드 우선순위
0, // 스택 크기
CREATE_SUSPENDED // 일시 정지 상태로 시작
);
if (pThread != NULL)
{
pThread->ResumeThread(); // 스레드 시작
}
}
3. 스레드와 UI 상호작용
스레드 내부에서 처리된 결과로 UI를 변경하고자 하는 경우에는 PostMessage나 SendMessage를 이용하여 메시지를 보내고, UI를 업데이트하는 방식으로 상호작용해야 한다.
예시: 스레드에서 UI 메시지 보내기
UINT CMyThread::Run()
{
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd(); // 메인 윈도우 얻기
for (int i = 0; i < 5; ++i)
{
// 메시지를 보내 UI를 갱신
pFrame->PostMessage(WM_USER + 1, 0, 0);
Sleep(1000);
}
return 0;
}
예시: UI에서 메시지 처리하기
메인 윈도우에서 메시지를 처리하는 방법은 다음과 같다.
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_MESSAGE(WM_USER + 1, &CMainFrame::OnThreadUpdate)
END_MESSAGE_MAP()
LRESULT CMainFrame::OnThreadUpdate(WPARAM wParam, LPARAM lParam)
{
// 스레드에서 전달된 메시지 처리
AfxMessageBox(_T("Thread message received"));
return 0;
}
4. 스레드 동기화
스레드가 여러 자원에 접근할 때 충돌을 방지하기 위해 동기화가 필요하다. MFC에서는 CMutex, CCriticalSection 등의 동기화 객체를 사용할 수 있다.
예시: CCriticalSection을 사용한 동기화
CCriticalSection cs; // 전역 크리티컬 섹션 객체
UINT CMyThread::ThreadProc(LPVOID pParam)
{
for (int i = 0; i < 5; ++i)
{
cs.Lock(); // 크리티컬 섹션 시작
// 공유 자원 접근
AfxMessageBox(_T("Thread is accessing shared resource"));
cs.Unlock(); // 크리티컬 섹션 끝
Sleep(1000);
}
return 0;
}
5. MFC에서 스레드 종료 처리
스레드를 종료할 때는 CWinThread::PostThreadMessage를 사용하여 종료 요청을 보내거나, 스레드 내에서 종료 조건을 확인하여 종료한다.
예시: 스레드 종료 요청
void CMainFrame::OnClose()
{
// 스레드 종료 처리
CMyThread* pThread = (CMyThread*)AfxGetThread();
pThread->PostThreadMessage(WM_QUIT, 0, 0);
CFrameWnd::OnClose();
}
결론
스레드를 생성하고 관리하는 방법, UI와의 상호작용, 그리고 스레드 간의 동기화와 종료 처리까지 스레드의 핵심 부분을 정리하였다.