[ MFC ] AfxBeginThread를 사용한 Timer 예제
🌈 프로그래밍/MFC

[ MFC ] AfxBeginThread를 사용한 Timer 예제

반응형

 

안녕하세요? 수구리입니다.

오랜만에 MFC를 다루게 되었습니다.

이번 포스팅에서는 Thread와 관련하여 포스팅을 하게 되었습니다.

Thread에서 가장 중요한 것은 바로 Sleep 함수입니다.

보통 Thread를 돌린다고 한다면, 거의 대부분 무한루프를 돌리는 경우가 많습니다.

따라서, 만약 Sleep이 없이 여러 Thread를 돌리게 된다면 어떤 특정 Thread가 PC의 리소스(자원)를 독점하여 사용하므로 다른 Thread는 PC의 자원을 할당받지 못해 정상적으로 동작하지 않게 됩니다. 

따라서 각 Thread마다 일정 시간 동안의 Sleep을 주어서 모든 Thread에게 골고루 자원을 나누어 주어야 합니다.

이번 예제에서는 현재 날짜와 시간에 대한 정보를 가져오는 Timer Thread에 대한 예제로,

많은 Thread를 다루지는 않지만 기본적으로 Thread를 생성하기 위한 방법과 생성, 그리고 소멸에 대해서 알아보도록 하겠습니다.

 

[ MFC 프로젝트 생성 ]

우선, 시작하기에 앞서 MFC의 프로젝트를 하나 생성해주어야 합니다.

생성하는 방법은 이전 포스팅 글을 참조해주세요

2021.08.06 - [프로그래밍/MFC] - MFC 프로그래밍 시작하기

 

MFC 프로그래밍 시작하기

C나 C++는 대학교때 수업도 듣고 많이 봐와서 익숙한데 MFC가 뭘까 ? 첫 포스트로 MFC에 대하여 간단하게 알아본 뒤 Visual Studio 2015에서 MFC 프로젝트를 만들어보고, 어떤 형태인지 알아보려고 한다.

tasddc.tistory.com

 

 

[ 기본 UI 구성 ]

우선 프로젝트를 생성한 후, 도구 상자에서 Static Text를 Drag & Drop으로 가져옵니다.

적절하게 크기를 설정하고 우클릭을 눌러 속성에 들어가서 변수 추가로 이름을 설정해 줍니다.

저는 m_staticDisp라고 이름을 지어주었습니다.

그리고 마찬가지로 시작과 종료 Button을 Drag & Drop으로 가져옵니다.

해당 Button들의 caption을 시작, 종료로 바꿔주고 각 버튼들을 더블클릭해서 함수를 생성해줍니다.

그러면 헤더 파일에 다음과 같이 코드가 생성됩니다.

// Thread_EXDlg.h : 헤더 파일
//
#pragma once
#include "afxwin.h"


// CThread_EXDlg 대화 상자
class CThread_EXDlg : public CDialogEx
{
// 생성입니다.
public:

// 생략

// 구현입니다.
protected:
	HICON m_hIcon;
    
    // 생성된 메시지 맵 함수
    // 생략
    
public:
    afx_msg void OnBnClickedButton1(); // 시작 버튼
    afx_msg void OnBnClickedButton2(); // 종료 버튼

};

 

그러면 해당 UI는 완성입니다. 시간을 표시할 Static Text를 만들었고,

Thread를 실행할 버튼과 종료할 버튼을 만들어주었습니다.

 

[ 변수 설정 및 초기화 ]

다음으로는 멤버 변수와 스레드 멤버 변수를 만들어보겠습니다.

위의 헤더 파일 public 부분에 아래 코드를 추가합니다.

public:
	// 생략
    CWinThread *p1;
    CString m_staticDisp;

 

스레드 멤버 변수를 초기화해주기 위해서 OnInitDialog 함수에 아래와 같이 변수를 초기화합니다.

BOOL CThread_EXDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
    
   	// 생략
    
    	// 이 대화 상자의 아이콘을 설정합니다.  응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
	//  프레임워크가 이 작업을 자동으로 수행합니다.
	SetIcon(m_hIcon, TRUE);			// 큰 아이콘을 설정합니다.
	SetIcon(m_hIcon, FALSE);		// 작은 아이콘을 설정합니다.

	// TODO: 여기에 추가 초기화 작업을 추가합니다.
	p1 = NULL;

	return TRUE;  // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}

참고로 저는 프로젝트 이름을 CThread_EX라고 지어주었습니다.

 

[ Thread 생성하기 ]

그러면 이제 위의 UI 구성에서 만든 생성 버튼을 다시 더블클릭해서 함수로 이동합니다.

이동하여 Thread를 생성하는 코드를 추가합니다.

void CThread_EXDlg::OnBnClickedButton1()
{
	p1 = AfxBeginThread(TimeThread, this);

	if (p1 == NULL) {
		AfxMessageBox(L"Error!");
	}
}

여기서 TimeThread라는 함수는 아직 만들어 주지 않았기 때문에 빨간 줄이 뜨는 것은 당연

그러고 TimeThread라는 함수를 사용하기 위해서 다시 아까 스레드 멤버 변수를 선언해 준 헤더 파일로 이동합니다.

아래의 코드를 추가합니다. 반드시 static 함수로 선언해 주어야 합니다.

// 헤더파일

// 생략
public:
	CWinThread *p1;
	CString m_staticDisp;
	afx_msg void OnBnClickedButton1();
	afx_msg void OnBnClickedButton2();
	static UINT TimeThread(LPVOID _mothod);
    
};

헤더 파일에서 정의를 해주었으니 다시 cpp (구현 파일)로 돌아와서 이 함수가 어떤 동작을 하는지 구현해줍니다.

 

// 구현 파일

UINT CThread_EXDlg::TimeThread(LPVOID _mothod)
{
	CThread_EXDlg *fir = (CThread_EXDlg*)_mothod;
	while (1) {
		CTime cTime = CTime::GetCurrentTime();
		fir->m_staticDisp.Format(_T("%d년 %d월 %d일\n%d시 %d분 %d초"), 
            cTime.GetYear(), cTime.GetMonth(), cTime.GetDay(),
			cTime.GetHour(), cTime.GetMinute(), cTime.GetSecond());
		fir->SetDlgItemText(IDC_STATIC_DISP, fir->m_staticDisp);
		Sleep(1000);
	}
	return 0;
}

 

[ Thread 종료하기 ]

마지막으로는 종료 버튼을 눌렀을 때, Thread를 종료하는 코드입니다.

종료 버튼을 더블클릭해서 함수로 이동한 뒤 코드를 작성합니다.

void CThread_EXDlg::OnBnClickedButton2()
{
	// thread 중지
	if (NULL != p1) {
		::SuspendThread(p1->m_hThread);
	}
	
}

 

[ 실행 화면 ]

초기 화면

 

시작 버튼클릭

 

빌드 후 디버깅 모드로 실행하면 1초에 한 번씩 시간을 갱신해 주고,

종료 버튼을 누르면 마지막 시간을 표시한 상태로 멈춰있게 됩니다.

정석적인 Thread 생성 방법이 아니긴 하지만 기본 예제로 공부하기 좋은 것 같습니다.

(실행 화면 추가 예정 - 10/21 완료)

이상으로 AfxBeginThread를 사용한 Timer 예제에 대해서 알아보았습니다.

감사합니다.

 

 

 

반응형