2014년 7월 11일

멀티 쓰레드 클래스 디자인

전역 함수를 쓰레드의 진입주소로 지정하여 멀티쓰레딩 시키는 구현하는 방식은 매우 쉽지만, 특정한 클래스의 멤버 함수를 쓰레드로 동작시키려면 몇가지 고려해야 할 사항이 있다.

특히 멤버 함수는 컴파일 시간에 함수의 주소를 알수 없기 때문에, 이를 해소하기 위해 멤버 함수를 static 으로 지정하는등의 꼼수(?)를 사용하기도 한다. 그러나 이럴 경우 static 함수내에서 접근이 가능한 멤버 변수들은 오직 static이어야 하다는 등의 여러 제약사항이 존재한다.

때문에 클래스의 멤버 함수를 쓰레드로 동작시키는 간단한 인터페이스 구조를 사용하여 이를 해결해 보자.


1. 우선 쓰레드로 동작할 녀석일 경우 아래 인터페이스를 상속받도록 한다.


class Threading
{
public:
  virtual int __stdcall execute()=0;
};

즉 사용자가 만든 클래스는 이 인터페이스를 상속받아 execute 를 구현하도록 한다. 당연히 execute 함수내부에서는 this 에 접근이 가능할뿐 아니라,  멀티쓰레딩에 안전하도록 고안하여 구현한다.

2. Threading 인터페이스를 관리하는 녀석을 만들자

class Thread
{
public:
  bool  execute();
  bool  join(DWORD timeout);

  Threading*  _pThreading;

  explicit Thread(Threading* pThreading)
    : _pThreading(pThreading)
  {
  }

  ~Threading();
};


이 녀석은 쓰레드를 시작, 종료대기하는 녀석으로 생성자에서 1번의 인터페이스를 받아 저장한후 execute 에서 쓰레드를 가동시킨다.

실제 구현내용을 살펴보면 ...

unsigned __stdcall FnThread(LPVOID pArg)
{
Thread* pThread = static_cast<Thread*>(pArg);
Threading* pThreading = static_cast<Threading*>(pThread->_pThreading);
        return _pThreading->execute();

}

bool Thread::execute()
{
  HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, FnThread, this, 0, NULL);
  //...
  return true;
}

bool Thread::join(DWORD timeout)
{
  return WAIT_OBJECT_0 = ::WaitForSingleObject(hThread , timeout);
}


정리하면 다음과 같은 순서로 작업하도록 한다.

  • Threading 인터페이스를 상속받아 쓰레드 동작 코드를 구현한다.
  • Threading 인스턴스를 Thread 의 생성자에 지정한다.
  • Threading.execute() 로 쓰레드를 가동한다.
  • 시그널/플래그 등의 조건으로 쓰레드를 정상종료시킨다
  • Threading.join() 로 종료대기및 자원회수를 한다.

ps. 위와 같은 구조는 Java의 Runnable 인터페이스나 .NET 의 프레임 워크와 매우 흡사함을 알 수 있다.


댓글 없음:

댓글 쓰기

시리우스 라이브러리 홈페이지 오픈

현재 시리우스(Sirius) 라이브러리라는 제품을 개발하고 이를 소개하는 홈페이지를 오픈 하였습니다. 관심있는 분들의 많은 방문 요청드립니다. 앞으로 업데이트 소식및 변경사항은 스파이럴랩 홈페이지를 통해 진행할 예정입니다. 스파이럴랩 홈페이지 :  h...