포스팅 계기
오늘은 개발자라면 다들 이해를 하고 있어야 하기도 하지만 제대로 이해하지 못하고 있는 사람도 많은 Thread에 관해서 설명도 하고 Windows API를 이용하여 만든 라이브러리로 공유할 겸 포스팅을 작성하게 되었다. 나는 주로 경로 탐색과 관련된 업무를 맡아서 진행을 하였는데 멀티 Thread로 업무를 맡아 진행했다. 추가로 Task Controller를 구현할 때에도 통신은 계속 들어오는데 일이 계속 쌓이는 경우들이 많아 Worker Thread를 많이 생성하여 여러 업무를 같이 진행하는 일도 많다 보니 Thread에 관해 부족하지만 실전 경험은 많은 편인 것 같다. 오늘 포스팅이 누군가에는 도움이 되는 포스팅이 되었으면 좋겠다.
프로세스(Process) 란?
Thread의 개념을 설명하기 위해서는 Process에 관한 설명이 필요하다. Process란 무엇일까? 간단하게 설명을 하면 실행 중인 프로그램(Programm)이라고 할 수 있다. 아래와 같이 작업관리자에 들어가면 현재 실행 중인 프로세스를 확인할 수 있다. 우리가 어떠한 프로그램을 실행할 때 해당 프로그램은 OS로부터 공간을 할당받고 실행되게 된다. 그리고 그 프로세스는 최소 1개의 Thread를 가지고 있다.
스레드(Thread) 란?
Thread는 프로세스 내부에서 실행되는 작업의 단위이다. 그 작업의 단위는 개발자가 Thread에게 업무 분배를 어떻게 하느냐에 따라 작은 작업 단위일 수도? 큰 작업 단위일 수도 있다. 예시가 적절하지 않을 수도 있지만 이해를 돕기 위해 일상생활에서 겪을 수 있는 상황으로 설명을 해보겠다.
우리가 새로운 프로젝트를 진행할 때 A팀이 맡아서 진행을 하게되었다고 가정을 한다. 새로운 프로젝트의 규모가 작아서 팀원 혼자서 진행을 할 수도 있고 여러 명의 팀원이 같이 진행을 할 수도 있다. 이때 팀원이 Thread라고 생각하면 좋을 것 같다. 만약 여러 팀원이 일을 같이 진행하게 되면 Multi Thread 개념이 된다고 할 수 있다. Multi Thread는 나중에 따로 포스팅을 하도록 하겠다.
해당 개념을 가지고 Windows.h를 활용하여 Thread Class를 만들어 보았다. 해당 Code는 참고하여 본인이 Custom해서 활용하면 좋을 것 같다.
스레드(Thread) Code
아래 코드는 Header 파일이며, Windows.h만 활용하여 사용하였다. 직접 구현할 필요가 없는 경우에는 <thread> lib을 활용하여 사용해도 된다. 나의 경우에는 lib을 사용하는 것도 좋지만 직접 구현을 통해 조금이라도 디버깅이 용이하기 위해서 필요한 기능만 구성하여 구현을 진행하는 편이다.
/*================================================================
** Description : Windows API Thread 를 편리하게 쓸 수 있게 만든 클래스
** Author : 초심을 찾자
** Create : 2024.01.05
** Last Modify : 2024.01.05
==================================================================*/
#pragma once
#include <Windows.h>
class CThread
{
private:
HANDLE m_hThread;
DWORD m_nThreadId;
static DWORD WINAPI StaticThreadStart(LPVOID IpParam);
//파생 클래스에서 미리 복사하지 않도록 방지
CThread(const CThread& rhs);
CThread& operator=(const CThread& rhs);
public:
CThread();
~CThread();
virtual DWORD Run() = 0; // 순수 가상 함수 파생 클래에서 구현 하도록 함
virtual const bool ThreadStart(const SIZE_T & stackSize = 0);
void ThreadStop();
virtual const __forceinline HANDLE GetThreadHandle() const;
virtual const __forceinline DWORD GetThreadId() const;
virtual const __forceinline bool IsRunning() const;
virtual void __forceinline Join();
virtual const __forceinline BOOL Yeild() const;
virtual void __forceinline Sleed(const DWORD& rMilliSecond);
virtual const __forceinline DWORD Suspend() const;
virtual const __forceinline DWORD Resume() const;
};
소스 코드
#include "CThread.h"
/************************************************
@함수명 : StaticThreadStart
@기능 : Thread를 Run 하는 함수
@입력값 : 없음
@출력값 : DWORD
@비고 : CreateThread를 진행할때 호출되는 함수이며,
Run 함수는 순수가상함수로 각자 상속받는 클래스 내부에
필수로 구현이 되어야 한다.
*************************************************/
//===============================================
DWORD CThread::StaticThreadStart(LPVOID IpParam)
//===============================================
{
CThread *pThread = static_cast<CThread*>(IpParam);
return pThread->Run();
}
//===============================================
CThread::CThread() :
m_hThread(NULL),
m_nThreadId(0)
//===============================================
{
}
//===============================================
CThread::CThread(const CThread & rhs)
//===============================================
{
// 아무것도 하지 않음 Thread 복제를 맡기 위해 컴파일러에게 맡기지 않기 직접 구현 후 내부는 비워둠
}
//===============================================
CThread::~CThread()
//===============================================
{
if (NULL != m_hThread)
{
CloseHandle(m_hThread);
}
}
//===============================================
CThread & CThread::operator=(const CThread & rhs)
//===============================================
{
// 아무것도 하지 않음 Thread 복제를 맡기 위해 컴파일러에게 맡기지 않기 직접 구현 후 내부는 비워둠
return *this;
}
/************************************************
@함수명 : ThreadStart
@기능 : Thread를 생성하는 함수
@입력값 : 없음
@출력값 : bool ( 생성 성공 여부 )
@비고 : CreateThread를 진행할때 호출되는 함수이며,
Run 함수는 순수가상함수로 각자 상속받는 클래스 내부에
필수로 구현이 되어야 한다.
*************************************************/
//===============================================
const bool CThread::ThreadStart( const SIZE_T & stackSize)
//===============================================
{
if (NULL != m_hThread)
{
// 이미 생성되어 있다면, 쓰레드가 종료되기 까지 기다렸다가 일정 시간이 지나면 그냥 생성 실패로 전달
if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 0))
{
return false;
}
CloseHandle(m_hThread);
}
m_hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)CThread::StaticThreadStart,
this,
0,
&m_nThreadId);
if (NULL != m_hThread)
{
return true;
}
else
{
return false;
}
}
/************************************************
@함수명 : ThreadStop
@기능 : Thread 동작을 멈추는 함수
@입력값 : 없음
@출력값 : 없음
@비고 : 없음
*************************************************/
//===============================================
void CThread::ThreadStop()
//===============================================
{
if (true == this->IsRunning())
{
::TerminateThread(m_hThread, -1);
}
if (NULL != m_hThread)
{
CloseHandle(m_hThread);
m_hThread = NULL;
}
}
/************************************************
@함수명 : GetThreadHandle
@기능 : Thread Handle을 반환해주는 함수
@입력값 : 없음
@출력값 : HANDLE
@비고 : 없음
*************************************************/
//===============================================
const HANDLE CThread::GetThreadHandle() const
//===============================================
{
return m_hThread;
}
/************************************************
@함수명 : GetThreadId
@기능 : Thread Id를 반환하는 함수
@입력값 : 없음
@출력값 : DWORD
@비고 : 추후 ThreadId를 관리할 수 있을 것 같아 확
장성 측면을 고려하여 구현
*************************************************/
//===============================================
const DWORD CThread::GetThreadId() const
//===============================================
{
return m_nThreadId;
}
/************************************************
@함수명 : IsRunning
@기능 : Thread가 생성되어 동작중인지 확인하는 함수
@입력값 : 없음
@출력값 : bool ( 동작 여부 )
@비고 : 없음
*************************************************/
//===============================================
const bool CThread::IsRunning() const
//===============================================
{
if (NULL != m_hThread)
{
DWORD exitCode = 0;
::GetExitCodeThread(m_hThread, &exitCode);
if(STILL_ACTIVE == exitCode)
{
return true;
}
}
return false;
}
/************************************************
@함수명 : Join
@기능 : 쓰레드들이 실행을 종료될 때 까지 대기하는
함수
@입력값 : 없음
@출력값 : 없음
@비고 : 없음
*************************************************/
//===============================================
void CThread::Join()
//===============================================
{
::WaitForSingleObject(m_hThread, INFINITE);
}
/************************************************
@함수명 : Yeild
@기능 : 현재 진행중인 작업을 멈추고 다른 Thread
에게 우선순위를 양보하는 함수
프로세스별로 관리되는 부분임을 명심
@입력값 :
@출력값 : bool ( 반환 성공 여부 )
@비고 :
*************************************************/
//===============================================
const BOOL CThread::Yeild() const
//===============================================
{
return ::SwitchToThread();
}
/************************************************
@함수명 : Sleed
@기능 : 특정 시간 동안 Thread 동작을 멈추는 함수
@입력값 : DWORD Thread를 잠시 멈추기 원하는 시간
@출력값 : 없음
@비고 : 없음
*************************************************/
//===============================================
void CThread::Sleed(const DWORD & rMilliSecond)
//===============================================
{
::Sleep(rMilliSecond);
}
/************************************************
@함수명 : Suspend
@기능 : Thread 동작을 멈추는 함수
@입력값 : 없음
@출력값 : DWORD ( 정지 성공 여부 )
@비고 : 없음
*************************************************/
//===============================================
const DWORD CThread::Suspend() const
//===============================================
{
return ::SuspendThread(m_hThread);
}
/************************************************
@함수명 : Resume
@기능 : 정지되어 있는 Thread를 다시 동작하도록
명령하는 함수
@입력값 : 없음
@출력값 : DWORD ( 재개 성공 여부 )
@비고 : 없음
*************************************************/
//===============================================
const DWORD CThread::Resume() const
//===============================================
{
return ::ResumeThread(m_hThread);
}
'C++ 개발이야기' 카테고리의 다른 글
C++ 개발자의 반성 (2) | 2025.04.16 |
---|---|
실무에서 경험한 C++ 멀티스레딩과 스마트 포인터 문제 및 해결 방법 (0) | 2025.03.05 |
C++ 개발자로서 성장하는 법: 나의 경험과 팁 (0) | 2025.03.05 |
C++ 나만의 코딩 습관 (2) | 2024.10.07 |
멀티 쓰레드(Multi Thread) (0) | 2024.01.17 |