검색하기귀찮아서만든블로그

AfxBeginThread 이벤트 클래스 본문

개발

AfxBeginThread 이벤트 클래스

hellworld 2019. 6. 30. 20:06

/*
제가 주로 사용하는 스레드 클래스입니다.
단일 스레드 구조로 만들었고 WaitForSingleObject 보다 WaitForMultiObject 를 주로 사용합니다.
스레드는 종료 이벤트와 사용자 이벤트를 대기합니다. 100ms 마다 타임아웃 동작을 합니다.
스레드 종료 시 최대 1초까지 스레드 종료를 대기하는 구조로 되어있습니다.


1. 스레드 생성 : MakeThread()
2. 스레드 사용자 동작 : SetUserThreadEvent()
3. 스레드 종료 : KillThread()
*/

#include "stdafx.h"
#include "afxwin.h"

///////////////////////////////////////////////////////////////////////////
// 헤더에 정의해도 무방

// 스레드 파라미터 구조체
typedef struct _stThreadParam
{
	CWnd* pParent; //  클래스 포인터
	HANDLE hProcEvent; // 스레드 동작 이벤트 
	HANDLE hEndEvent;  // 스레드 종료 이벤트
	BOOL bThreadEnd;  // 스레드 종료 여부
	void* pThread; // 스레드 클래스 포인터 
    
 	// 구조체 생성자
	_stThreadParam()
	{
		pParent = NULL;
		hProcEvent = NULL;
		hEndEvent = NULL;
		bThreadEnd = FALSE;
		pThread = NULL;
	}
	// 구조체 소멸자 
	~_stThreadParam()
	{
		// 스레드 이벤트들을 소멸한다.
		SAFE_CLOSEHANDLE(hProcEvent);
		SAFE_CLOSEHANDLE(hEndEvnet);
	}
}STThreadParam, *LP_STThreadParam;

// 포인터 삭제 메소드
#define SAFE_DELETE(x)	{if(x) delete x; x=NULL;}
#define SAFE_CLOSEHANDLE(x)	{if(x) CloseHandle(x); x=NULL;}
///////////////////////////////////////////////////////////////////////////

// 상수 정의
const UINT THREAD_EVENT_COUNT = 2; // 스레드 이벤트 카운트
const UINT THREAD_WAIT_TIMEOUT = 100; // 스레드 대기 시간 ms

// 글로벌 변수 (클래스 멤버 변수로 정의 해도 무방)
LP_STWebCheckerThreadParam m_pThreadParam = NULL; // 스레드 파라미터 저장용 구조체 포인터

/*============================================================================
* 함   수 : MakeThread
*---------------------------------------------------------------------------
* 출   력 : void
*---------------------------------------------------------------------------
* 설   명 : 클래스 생성자
*===========================================================================*/
CTestClassName::CTestClassName()
{
	m_pThreadParam = NULL;
}

/*============================================================================
* 함   수 : MakeThread
*---------------------------------------------------------------------------
* 출   력 : void
*---------------------------------------------------------------------------
* 설   명 : 클래스 소멸자
*===========================================================================*/
CTestClassName::~CTestClassName()
{
	KillThread();
}

/*============================================================================
* 함   수 : _ThreadProc
*---------------------------------------------------------------------------
* 출   력 : UINT WINAPI
*---------------------------------------------------------------------------
* 설   명 : 스레드 프로시저
*===========================================================================*/
UINT WINAPI CTestClassName::_ThreadProc(LPVOID pParameter)
{
	LP_STWebCheckerThreadParam pParam = (LP_STWebCheckerThreadParam)pParameter;
	CTestClassName* pMain = (CTestClassName*)pParam->pParent;
	HANDLE hEvents[THREAD_EVENT_COUNT] = { pParam->hEndEvent, pParam->hProcEvent };
	DWORD dwEvent = WAIT_FAILED;

	while (TRUE)
	{
		dwEventRet = WaitForMultipleObjects(THREAD_EVENT_COUNT, hEvents, FALSE, THREAD_WAIT_TIMEOUT);

		// 대기 실패, 또는 종료이벤트가 발생하면 while 루프를 빠져나간다.
		if (WAIT_FAILED == dwEventRet || WAIT_OBJECT_0 == dwEventRet)
		{
			break;
		}

		// 사용자가 설정한 이벤트가 발생했을 때
		if (WAIT_OBJECT_0 + 1 == dwEventRet)
		{
			///////////////////////////////////////////////
			// 이벤트가 발생했을 때 동작을 구현하시면 되요 ~
			// 클래스에 접근하시고 싶으면 pMain 를 사용하시면 됩니다.
			///////////////////////////////////////////////
			continue;
		}

		///////////////////////////////////////////////
		// 타임아웃 되었을 때 동작 구현하시면 되요 ~
		// 아무것도 안할 꺼면 THREAD_WAIT_TIMEOUT 값을 INFINITE 로 변경하세요.
		///////////////////////////////////////////////
	}

	// 스레드가 종료 된다는 플래그를 TRUE 로 바꿔준다. (종료를 대기하고 있는 프로세스를 위해서)
	pParam->bThreadEnd = TRUE;
	return 0;
}

/*============================================================================
* 함   수 : MakeThread
*---------------------------------------------------------------------------
* 출   력 : void
*---------------------------------------------------------------------------
* 설   명 : 스레드 생성
*===========================================================================*/
void CTestClassName::MakeThread()
{
	// 기존에 생성된 스레드가 있을 경우 종료 시킨다.
	KillThread();

	// 스레드 파라미터 구조체 생성
	LP_STWebCheckerThreadParam pParam = new STWebCheckerThreadParam;

	// 파라미터 구조체에 클래스 포인터와, 스레드 동작 이벤트, 스레드 종료이벤트를 생성한다.
	pParam->pParent = this;
	pParam->hProcEvent = CreateEvent(TRUE, NULL, NULL FALSE);
	pParam->hEndEvent = CreateEvent(TRUE, NULL, NULL FALSE);

	// 스레드 생성
	CWinThread* pThread = (void*)AfxBeginThread((AFX_THREADPROC)_ThreadProc, (void*)pParam);
	if (pThread == NULL)
	{
		return;
	}

	pParam->pThread = (void*)pThread;
	// 스레드 파라미터를 멤버 포인터 변수에 기록한다.
	m_pThreadParam = pParam;
}

/*============================================================================
* 함   수 : MakeThread
*---------------------------------------------------------------------------
* 출   력 : void
*---------------------------------------------------------------------------
* 설   명 : 스레드에게 사용자가 지정한 동작을 하도록 이벤트를 활성화 한다.
*===========================================================================*/
void CTestClassName::SetUserThreadEvent()
{
	// 스레드 파라미터 포인터가 NULL 이면 함수를 반환한다. (예외 처리)
	if (NULL == m_pThreadParam)
	{
		return;
	}

	SetEvent(m_pThreadParam->hProcEvent);
}


/*============================================================================
* 함   수 : MakeThread
*---------------------------------------------------------------------------
* 출   력 : void
*---------------------------------------------------------------------------
* 설   명 : 스레드를 종료하도록 이벤트를 활성화 한다.
*===========================================================================*/
void CTestClassName::KillThread()
{
	// 스레드 파라미터 포인터가 NULL 이면 함수를 반환한다. (예외 처리)
	if (NULL == m_pThreadParam)
	{
		return;
	}
	// 스레드 종료 이벤트를 활성화 한다.
	SetEvent(m_pThreadParam->hEndEvent);

	// 스레드가 종료 될 때 까지 최대 1초를 대기한다.
	for (int i = 0; i < 100 && FALSE == m_pThreadParam->bThreadEnd; i++)
	{
		Sleep(10);
	}
    
	// 스레드가 종료되지 않을 경우 강제 종료한다.
    // 참고 : 스레드가 빨리 종료되지 않고 여기서 강제 종료될 경우 메모리릭 발생 소지 있음
    // 가급적이면 스레드의 처리 루틴을 간결하게 작성하여 종료 이벤트를 빨리 처리할 수 있도록 해야 한다.
	if (FALSE == m_pThreadParam->bThreadEnd)
	{
		CWinThread* pThread = (CWinThread*)m_pThreadParam->pThread;
		DWORD dwExitCode = 0;
		GetExitCodeThread(pThread->m_hThread, &dwExitCode);
		TerminateThread(pThread->m_hThread, dwExitCode);
	}

	// 스레드 파라미터 객체를 소멸한다.
	SAFE_DELETE(m_pThreadParam);
}