Programming/Others

MFC 기반의 Multi-threading

EveryDayIsNewDay 2025. 1. 8. 11:34

근 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와의 상호작용, 그리고 스레드 간의 동기화와 종료 처리까지 스레드의 핵심 부분을 정리하였다.