이전포스트

[C++] CreateThread / _beginthread / _beginthreadex 의 설명

freemmer 2010. 11. 2. 15:21
원문 : http://mgun.tistory.com/653?srchid=BR1http%3A%2F%2Fmgun.tistory.com%2F653

스레드를 생성하는 함수.
스레드는 커널에 의해 생성되는 리소스이므로 커널 오브젝트가 생성될 것이고 함수 호출이 끝나면
커널 오브젝트를 의미하는 핸들이 리턴 될 것이다.

1. Win32 API의 CreateThread 대신 C Run Time Library의 _beginthreadex를 써야하는 경우

1.부동 소수형 변수나 함수를 사용할 경우
2.C의 malloc과 free나 C++ 의 new와 delete 를 사용할경우
3.stdio.h 나 io.h에서 어떤 함수를 호출한다면
4.strtok() 나 rand() 와 같이 정적 버퍼를 사용 하는 어떤 런타임 함수를 호출할 경우

 4번에 대한 부연 설명
c 런타임 함수들중 전역 자원을 쓰는 함수들이 있습니다.
strtok 같은것이 예이죠.
이런경우 멀티스레드 환경에서 문제가 될수 있기때문에 c 런타임 함수들을 위한 각각의 전역자원공간을 스레드마다 할당하는 함수가 _beginthread , _beginthreadex 라고 알고 있습니다.
이 함수들도 내부적으로는 CreateThread를 호출하죠.
출처 : http://kldp.org/node/46904

위의 4가지 중 어떠한 동작도 수행하지 않는다면, 단일 쓰레드 라이브러리와 CreateThread함수를 사용하는 것이 안전합니다.



2. _beginthread 대신 _beginthreadex를 사용하는 이유

_beginthread 는 쓰레드를 생성하고 바로 CloseHandle을 호출하므로, 이 때 반환되는 핸들로는 쓰레드 오브젝트와 통신할 수 없습니다. 이렇게 _beginthread가 동작하는 것은 Win32의 상세함을 숨기기 위해 고안되었지만 결국 버그가 되버린 함수가 되버렸고, _beginthreadex에서 수정되었습니다.
출처 : http://www.fiadot.com/TatterTools/index.php?pl=476&setdate=200402&PHPSESSID=871516866c7e75a29140526552790ef2

3. _endthread_endthreadex를 명시적으로(explicitly) 호출할까 말까?
_beginthread 는 _endthread와, _beginthreadex는 _endthreadex와 각각 짝을 이룹니다. _endthread와 _endthreadex는 스레드가 인자로 쓰인 루틴에서 되돌아올 때 자동으로 호출되나 명시적으로 호출할 수 있습니다.

_endthread : Win32 CloseHandle API 처럼 자동으로 스레드 핸들을 닫습니다.
따라서 Win 32 CloseHandle API 불러 명시적으로 스레드 핸들을 닫으면 안됩니다.

_endthreadex : Win32 ExitThread API 처럼 스레드 핸들을 닫지 않습니다.
따라서 Win 32 CloseHandle API 호출에 의한 스레드 핸들을 닫아야 합니다.

결론 
_endthread와 _endthreadex는 모두 명시적으로 호출하지 않아도 자동으로 호출되지만, _beginthread를 사용했을 경우에는 CloseHandle를 호출하면 Exception이 발생합니다(Window NT 이상).
반면에 _beginthreaex를 사용했을 경우에는 반드시 CloseHandle를 호출해야 합니다.

Note : Libcmt.lib와 연관된 실행파일은 Win32 ExitThread API를 호출하면 안됩니다. 이는 스레드에 할당된 자원 회수를 방해합니다. _endthread 와 _endthreaex는 스레드에 할당된 자원을 회수한 수 ExitThread를 부릅니다.
출처 : http://msdn2.microsoft.com/ko-kr/library/hw264s73(VS.80).aspx


unsigned long _beginthreadex(
void* security,                                  // security descriptor
unsigned stack_size,                        // initial stack size
unsigned (*start_address)(void*),      // thread function
void* arglist,                                    // thread argument
unsigned initflag,                              // creatioin option
unsigned* thrdaddr                           // thread identifier
);

인자를 보면 CreateThread함수와 전달하는 인자의 수와 지니는 의미가 완전 같다.

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
unsigned WINAPI ThreadFunction(void* arg);

int _tmain(int argc, _TCHAR* argv[])
{
 HANDLE hThread;
 DWORD dwThreadID;
 // thread를 생성.
 hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunction, NULL, 0, (unsigned*)&dwThreadID);
 if(hThread == 0)
 {
  puts("_beginthreadex() error");
  exit(1);
 }
 printf("생성된 스레드의 핸들 : %d\n", hThread);
 printf("생성된 스레드의 ID : %d\n", dwThreadID);
 Sleep(3000); 
 
 printf("main 함수 종료");
 //getchar();
 return 0;
}
unsigned WINAPI ThreadFunction(void* arg)
{
 // main thread 이외의 thread. beginthreadex로 생성.
 int i;
 for(i=0; i<5; i++)
 {
  Sleep(2000);
  puts("스레드 실행 중");
 }
 return 0;
}


위 예제의 결과는 위의 사진과 같다.
이 예제에서는 _beginthreadex 함수를 사용하여 main thread 이외의 스레드를 만든다.
하지만 문제가 하나 있는데 이는 threadFunction를 호출하는 스레드가 메인스레드가 먼저 끝나버림으로 인해
제대로 완수되지 못해 5번의 for문중 한번밖에 돌지 못했다는 것이 그것이다.
메인스레드가 종료되면 프로세스도 종료가 되어버리는 것이다.
이 문제의 해결은 메인스레드가 다른 스레드가 종료될 때 까지 메인스레드를 기다려 주면 된다.
반응형