윈도우즈에서 스레드를 생성하는 API는 CreateThread(), _beginthread(), _beginthreadex() 이렇게 3개의 함수가 존재합니다. 

CreateThread()와 _beginthread(), _beginthreadex()의 차이점은 다음과 같습니다.
  • CreateThread()는 스레드를 생성하는 기능만 담당한다.
  • _beginthread(), _beginthreadex()는 내부적으로 CreateThread() 를 사용하여 스레드를 생성하고 C Runtime library에서 내부적으로 필요로 하는 메모리 영역을 초기화 해주는 역할을 하게 됩니다. 초기화 되는 메모리 영역은 각 스레드 마다 따로 관리되게 됩니다.


그렇다면 _beginthread(), _beginthreadex()의 차이는 무엇일까요? 의외로 이 차이에 대해서 대답을 명쾌하게 해주는 분이 많지가 않습니다. 이제부터 그 차이를 설명해 보도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
uintptr_t _beginthread( 
   void( *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

함수 원형을 보고 알 수 있는 파라메터에 의한 차이점은 다음과 같습니다.

  • _beginthreadex()는 security를 이용하여 보안 관련 설정을 할 수 있다.
  • _beginthreadex()는 initflag를 이용하여 스레드의 초기 동작을 정의 할 수 있다.
  • _beginthreadex()는 thrdaddr을 이용하여 thread id를 받을 수 있다.
  • _beginthreadex()는 __stdcall 형식의 함수 포인터를 thread 실행 함수로 받는다.
  • _beginthreadex()는 함수 실행 실패시에 0을 리턴한다. (_beginthread는 -1을 리턴한다.)

여기에 더해서 가장 중요하게 기억해야 할 한가지의 차이점이 더 존재합니다. (이 내용이 글을 쓰는 이유이기도 합니다. ^^)
_beginthread()는 스레드가 생성되고 스레드 함수의 실행이 종료 되면 스레드의 정리 작업을 해주게 됩니다. 이러한 형태는 편리한 점이 있지만 단점도 존재하게 되는데 바로 스레드의 실행 완료후에 해당 스레드의 정보를 조회 할 수 있는 방법이 없다는 것입니다. 

예를 들어 스레드의 종료시에 exit 코드가 무엇인지 알기 위해서 GetExitCodeThread()를 사용할 수 있습니다. 이때 첫번째 인자로 스레드의 핸들을 파라메터로 넘겨줘야 되는데 _beginthread()로 생성된 스레드의 핸들은 GetExitCodeThread()를 사용할 수 없습니다. 

1
2
3
4
BOOL WINAPI GetExitCodeThread(
  HANDLE hThread,
  LPDWORD lpExitCode
);

왜 그럴까요? _beginthread()로 생성된 스레드 핸들은 스레드 함수의 실행이 종료될 때 _beginthread() 내부에서 CloseHandle()을 호출하기 때문에 프로그래머가 GetExitCodeThread()를 사용할 수 있는 시점에서는 이미 소멸된 핸들이 되기 때문입니다.

_beginthreadex()는 위와 같은 문제를 해결하기 위해 스레드가 종료될때 내부적으로 CloseHandle()를 호출하지 않고 사용자가 명시적으로 해제하도록 변경되었습니다.

결과적으로 내용을 정리하면 다음과 같습니다.

  • _beginthread()로 생성된 스레드 핸들은 스레드 종료시에 CloseHandle()이 내부적으로 호출되어 신경쓸 필요가 없지만 스레드 함수 완료 후 스레드 핸들을 이용한 어떠한 API 함수도 실행 시킬 수 없습니다.
  • _beginthreadex()로 생성된 스레드 핸들은 스레드가 실행완료된 후 내부적으로 CloseHandle()을 실행시키지 않기 때문에 스레드 핸들을 이용한 API 함수를 실행할 수 있지만 사용자가 명시적으로 CloseHandle()을 호출해 주어야 합니다. 만약 CloseHandle()을 해주지 않으면 핸들이 계속 쌓이게 되는 Resource leak이 생기게 됩니다.
Posted by cyj4369
,