프로그래밍 관련/언어들의 코딩들 C++ JAVA C# 등..

C, C++ Thread, 스레드, 쓰레드 _beginthreadex(멀티스레드적합), _beginthread

AlrepondTech 2020. 9. 10. 04:35
반응형

 

 

=======================

=======================

=======================

 

 

 

출처: http://krids.tistory.com/187

 

사용되는 헤더

process.h

사용되는 함수

_beginthreadex, _endthreadex, WaitForSingleObject, GetExitCodeThread, CloseHandle, TerminateThread

 

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <Windows.h>
#include <process.h>


using namespace std;


unsigned int __stdcall BasicThread(void* arg) {
  int xxx = (int)arg;


  int result = 0;


  for (int x = 0; x < 100; ++x) {
    result += xxx++;
  }


  _endthreadex(result);
  return 0;
}


unsigned int _stdcall InfinityThread(void *arg) {


 while (true) {
   Sleep(1000);
 }


 return 0;
}


void main()
{
  unsigned int id;
  HANDLE hand;
  int xxx = 120;


  hand  = (HANDLE)_beginthreadex(NULL,0, BasicThread, (void *)xxx, 0, &id);
  hand2 = (HANDLE)_beginthreadex(NULL,0, INfinityThread, (void *)xxx, 0, &id);


  WaitForSingleObject(hand, INFINITE);


  DWORD test = 0;
  GetExitCodeThread(hand, &test);
  CloseHandle(hand);


  TerminateThread(hand2, 0);


  std::cout<< test <<std::endl;
}

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

출처: http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=255602395&qb=VGVybWluYXRlVGhyZWFkIENsb3NlSGFuZGxl&enc=utf8§ion=kin&rank=2&search_sort=0&spq=0&pid=TRc9IspySDNssutZyE4ssssss1h-324957&sid=Tp93ElcZ5Pq3lqdkO8f/CQ%3D%3D

 

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <process.h> 
  4. #include <windows.h> 
  5. unsigned int __stdcall BasicThread(void* arg)  
  6. int val = (int)arg; 
  7. int result = 0, i = 0; 
  8. for (i = 0; i < 100; ++i) 
  9. result += val++; 
  10. _endthreadex(result);  // 쓰레드 종료함수입니다. 
  11. return 0; 
  12. unsigned int _stdcall InfinityThread(void *arg)  
  13. while (true
  14. Sleep(1000); //1초대기합니다. 
  15. return 0; 
  16. void main() 
  17. unsigned int id; 
  18. HANDLE hand, hand2; 
  19. int xxx = 120; 
  20. hand = (HANDLE)_beginthreadex(NULL, 0, BasicThread, (void *)xxx, 0, &id);  //쓰레드를 생성시작합니다. BasicThread 함수가 새 쓰레드에서 호출됩니다. 
  21. hand2 = (HANDLE)_beginthreadex(NULL, 0, InfinityThread, (void *)xxx, 0, &id); //쓰레드 생서 시작입니다 InfinityThread 함수가 새 쓰레드에서 호출됩니다. 
  22. WaitForSingleObject(hand, INFINITE); //hand 핸들(스레드)의 이벤트를 무한대기합니다. 
  23. DWORD test = 0; 
  24. GetExitCodeThread(hand, &test); //hand 핸들(스레드)의  종료 코드를 얻어와서 test에 담습니다. 
  25. CloseHandle(hand); //핸들을 종료합니다(레퍼런스카운트 다운) 
  26. TerminateThread(hand2, 0); //hand2 핸들의 스레드를 강제종료 시킵니다. 
  27. printf("%ld\n", test); //test 값 출력 
  28. system("PAUSE"); //정지 

 

 

=======================

=======================

=======================

 

 

 

출처: http://3dmpengines.tistory.com/620

_beginthreadex와 _endthreadex 함수는 API 함수가 아니라 C Library 함수이다.

 

이 함수들에 관한 사용법을 알아보자...

 

API 함수와 매우 유사하다.

 

#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <tchar.h>

 

unsigned counter;

 

unsigned __stdcall SecondThreadFunc( void *pArg )
{
 _tprintf(_T(" * In second thread... \n") );
 while ( counter < 1000000 ) counter ++;
 _endthreadex(0);
 return 0;
}

 

int main()
{
 HANDLE hThread;
 unsigned threadID;

 

 _tprintf( _T(" * Creating second thread... \n ") );
 hThread = (HANDLE) _beginthreadex(NULL, 0, SecondThreadFunc, NULL, 0, &threadID );
 
 WaitForSingleObject( hThread, INFINITE );

 _tprintf( _T(" * Counter should be 1000000; it is -> %d\n"), counter );

 CloseHandle( hThread );

 

 return 0;
}

 

 

SecondThreadFunc의 원형은 API의 CreateThread로 부르는 함수(Createthread 함수의 세번째 인자)의 원형과 동일하다.

 

 

이 _beginthreadex 함수는 내부적으로 CreateThread 함수를 호출하고 있다.

 

이를 보고 싶다면 MS사에서 제공하는 코드를 직접 보면 될 것이다.

 

이 코드 파일은 첨부토록 하겠다. _beginthreadex 함수를 찾아보면 알 것이다.

 

또한, thread 생성과 파괴시 다음과 같은 함수들이 존재한다.

 

학습 : CreateThread), ExitThread()
C Library + API : _beginthreadex(), _endthreadex();
MFC : AfxBeginThread(), AfxEndThread();

 

 

 


 

http://blog.naver.com/limsk112/70107328764

 

 

CreateThread 함수는 Win32 API 에서 제공되는 함수고

_beginthread,  _beginthreadex 함수는 C / C++ Runtime-Library 에서 제공되는 함수다.

 

_beginthread 함수는  이 함수로 새로운 쓰레드를 생성하고 난 후 바로 ::CloseHandle( ) 함수를 호출해서 생성한 쓰레드의 핸들을 닫아 버려서 생성한 쓰레드 오브젝트와의 통신을 할 수가 없다. 핸들을 바로 닫아버리는 이유는 Win32의 상세함을 숨기기 위함인데 결국 버그가 되어 버린 함수이다.

 

 이를 고안해서 새로 탄생한 함수가 바로 _beginthreadex 함수이다.

_beginthreadex 는 내부적으로 새로 생성한 쓰레드의 핸들을 닫지 않기 때문에 명시적으로 ::CloseHandle( ) 함수를 호출하여 쓰레드의 핸들을 수동으로 닫아 주어야 한다.

_beginthreadex 의 리턴값은 새로 생성된 쓰레드의 핸들값이다. 하지만 자료형이 정확히 일치하지 않기 때문에 적절하게 형변환을 해줘야 한다.

 

 

_beginthread 함수와 짝을 이루는 것이 _endthread 함수인데, 생성한 쓰레드가 종료되면, 이 함수가 자동 호출 되어진다. 따라서 이 함수는 명시하지 않으며, 명시를 해주면 에러가 난다.

 

_beginthreadex 함수와 짝을 이루는 것은 _endthreadex 함수다. 이 함수는 생성한 쓰레드가 종료되면, 역시 이 함수가 자동 호출되어 진다. 따라서 이 함수는 명시하지 않아도 되지만, 명시를 해도 _endthread 함수처럼 에러가 나는건 아니다.

 

[출처] CreateThread 와 _beginthread, _beginthreadex 의 차이|작성자 체흐


[더 자세한 설명]- 아래까지 보는게 좋다

 

 

http://blog.naver.com/drags99/150032023121

 

두 함수는 모두 스래드를 생성할 때 호출하는 함수이다.

 

이 두 함수는 겉으로 보기엔 동일한 기능을 하지만 차이점이 있어서 이를 알고 넘어가야 한다.

MSDN을 보면 _beginthread 와 _beginthreadex 를 사용하면 스래드에 여러 인수를 전달할 수 있습니다라고 되어있다이러한 이유로 CreateThread 보다는 위 두 함수를 많이 이용한다.

위 두 함수 중에도 개인적으로는 _beginthreadex 함수를 주로 이용하고 있다.

위 두 함수의 차이점을 비교해 보면

함수 호출 규약이 _beginthread 함수는 __cdecl 이고, _beginthreadex 함수는 __stdcall 이다.

함수 호출 규약에 따라 내부적으로 인자 값들을 스택에 저장할 때 순서가 달라진다이러한 부분은 코딩을 하는 부분에서는 크게 신경 쓰지 않아도 되는 부분이다.

또 다른 차이점은 각각의 함수는 _endthread, __endthreadex 라는 함수와 같이 이용된다는 점이다딱 봐도 뒤에 ex 가 붙은 함수와 안 붙은 함수가 세트로 사용된다는 걸 쉽게 이해할 수 있을 것이다.

또 다른 차이점은 return Value 값이다.

_beginthread 함수의 경우 성공일 때 0, 실패일 때 -1을 리턴 하지만, _beginthreadex 함수는 실패일 때 0(NULL), 성공일 때 -1이 아닌 값을 리턴 한다.

_beginthread 함수는 성공과 실패를 구분하지만 _beginthreadex 함수는 실제 HANDLE 을 리턴하기 때문에 GetExitCodeThread 함수 호출 등이 가능하게 된다.

두 함수를 호출하는데 있어서 인자 값도 차이가 난다.

_beginthreadex 함수에서 initflag 값을 이용해 스래드가 생성되는 시점에서 스래드의 상태를 설정할 수 있다.

threadaddr 은 32bit 포인터 값으로 스래드의 주소 값을 반환 받을 수 있다.

Security 인자를 이용해 자식 프로세스에서 이 함수에서 반환된 스래드의 핸들을 상속받아서 사용할 수 있는지 유무를 결정할 수 있다.

위와 같은 차이가 나지만 대부분은 _beginthreadex 함수를 많이 이용할 것이다.

하지만 _beginthreadex 함수를 이용해야 하는 아주 결정적인 이유가 하나 더 있다.

Ansi 표준 라이브러리 함수를 호출할 경우 _beginthread 함수를 이용해 생성된 스래드 에서는 동기화 이유 때문에 안정성을 보장받지 못하게 되지만 _beginthreadex 함수를 이용할 경우 스래드별로 내부적으로 별도의 메모리 공간을 할당하기 때문에 동기화와 관련돼 안정성을 보장받을 수 있게 된다이렇게 스래드 생성시 할당된 별도의 메모리 공간을 해제하기 위해서 _endthreadex 함수와 짝을 이뤄서 이용하게 되는 것이다.

컴파일러에 따라 다르겠지만 .Net 에서는 멀티스래드에 안정적인 라이브러리를 제공하고 있기 때문에 별도의 선언이나 조작 없이도 안정적인 라이브러리를 이용할 수 있다고 한다.(아직까지는 별 문제 없이 잘 쓰고 있다.)

만약 _beginthreadex 함수로 스래드를 생성하고 _endthread 함수를 이용해 스래드를 종료하게 된다면 스래드 생성시에 할당된 메모리가 반환되지 않기 때문에 이렇게 스래드를 이용할 수 없다.

하지만 스래드를 생성할 때 인자로 넘긴 함수 포인터에서 return 을 이용해 스래드를 종료하게 되면 별도의 _endthreadex 함수를 호출하지 않아도 생성시에 할당된 메모리가 반환되기 때문에 return 을 이용하면 이 부분은 별로 걱정할 필요 없다.

스래드 종료 시 반환된 값은 내부에 생성된 스래드 커널에 저장되고, GetExitCodeThread 함수를 이용해 받아올 수 있다.

하지만 위 함수를 호출하기 전에 CloseHandle 함수를 이용해 스래드 핸들을 종료시킨다면 내부에 생성된 스래드 커널이 삭제되기 때문에 반환 값을 받을 수 없게 된다.

스래드의 반환 값을 이용할 필요가 있을 경우 GetExitCodeThread 함수를 이용해서 반환 값을 받아온 후에 CloseHandle 함수를 호출해 줘야 한다.

추가로 한가지 더 얘기 하면 CloseHandle 을 이용해서 함수 호출 시 반환된 스래드의 핸들을 종료시키게 되면 내부적으로 스래드 커널에 UC가 줄어들게 되 스래드가 종료되는 시점에서 스래드 커널도 바로 종료될 수 있다이 부분은 커널과 핸들러에 관한 내용을 미리 알고 있어야 해서 간단히 설명한다.

.. 두 함수를 비교하다 보니 결론적으로 코딩할때는 _beginthreadex 함수만 이용하게 된다.

솔직히 _endthreadex 함수도 거의 사용하지 않는다그냥 함수 내에서 return 을 이용해 스래드를 종료하고 있다.



출처: http://3dmpengines.tistory.com/620 [3DMP]

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://freemmer.tistory.com/36

 

 

원문 : 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() 와 같이 정적 버퍼를 사용 하는 어떤 런타임 함수를 호출할 경우

출처 : http://www.fiadot.com/TatterTools/index.php?pl=476&setdate=200402&PHPSESSID=871516866c7e75a29140526552790ef2


 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문중 한번밖에 돌지 못했다는 것이 그것이다.
메인스레드가 종료되면 프로세스도 종료가 되어버리는 것이다.
이 문제의 해결은 메인스레드가 다른 스레드가 종료될 때 까지 메인스레드를 기다려 주면 된다.

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://fingerdev.tistory.com/25

 

# Thread (쓰레드) 의 진실

 

   프로그래밍을 하다보면 쓰레드를 많이 사용하게 된다. 하지만 쓰레드는 굉장히 위험한 엔진?이므로 

   잘 돌려야 한다. 

     대부분 쓰레드를 생성할때 사용할 수 있는 함수들이 

     CreateThread 함수, _beginthread 함수, _beginthreadex 함수, AfxBeginThread 함수  

     이렇게 4가지 정도 사용되어지는데..

 

     * CreateThread 함수 

        : 쓰레드 생성 함수  (사용자제요망)

          C/C++ 표준함수를 호출하려하면 문제가 발생할 수 있음. 

 

     * _beginthread 함수 

        : C/C++ 표준함수가 안전하게 실행되어질 수 있다.

          하지만 생성시 반환되는 핸들을 무효화시켜 커널 오브젝트에 접근할 수 있는 방법을 

          막아버리는 문제점이 있다. 

 

     * _beginthreadex 함수 

        : C/C++ 표준함수가 안전하게 실행되어질 수 있다.  (사용권장) 

          마지막엔 CloseHandle 로 핸들을 닫아 주어야 한다.

           _beginthread 함수를 개선시킨 함수이다. 

             

 

     * AfxBeginThread 함수 

        : MFC 에서 제공되어지는 클래스/함수 등을 사용할 때에는 특히 UI 관련된 작업을 할때에는 

           반드시 AfxBeginThread 함수로 쓰레드를 생성하여야 함.  (사용권장) 

           내부적으로는 _beginthreadex 함수가 사용되어진다.

 

 

 

 

반응형

 

728x90

 

 

-------------------------------------------------------------------------------------------------

// _beginthreadex 예제

 

    #include <process.h>

 

   {

      HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, threadFunc, NULL, CREATE_SUSPENDED, NULL);

       if (!hThread)

      {

   TRACE(" Error Thread \r\n");

   return;

      }

      ResumeThread(hThread);

      SuspendThread(hThread);

      ResumeThread(hThread);

      WaitForSingleObject(hThread, INFINITE);

      CloseHandle(hThread);

   }

 

   unsigned __stdcall threadFunc( void* pArguments )

   {

      ...

      return 0;

   }

-------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------

// AfxBeginThread 예제

 

   #include <afxwin.h>

 

   CWinThread *pThread = AfxBeginThread(funcThread, NULL);

      

   static UINT funcThread(LPVOID pParam);

   UINT CT_ThreadAfxDlg::funcThread(LPVOID pParam)

   {

TRACE(" THREAD \r\n");

return 0;

   }

-------------------------------------------------------------------------------------------------



출처: http://fingerdev.tistory.com/25 [FingerDev (핑거데브)]

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://blog.naver.com/PostView.nhn?blogId=elastica&logNo=50046517683

 

위키피디아에서는 쓰레드를 아래와 같이 정의하고 있습니다.

 

스레드(thread)는 어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위를 말한다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다. 이러한 실행 방식을 멀티스레드(multithread)라고 한다.

 

사전적 의미대로 프로그램환경에서 멀티 작업을 위해 요긴하게 쓰여지는 것이 쓰레드 입니다.

 

윈도우 프로그램에서는 다양한 쓰레드 함수를 지원합니다.

Visual Studio 2005 ,유니코드 컴파일 기준으로 정리해 보았습니다.

 

1>CreateThread

Win32 시절부터 쓰레드 생성에 가장 보편적으로 많이 쓰이는 쓰레드 함수입니다.

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  SIZE_T dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId
);

 

각각의 파라미터는 아래와 같습니다.

- lpThreadAttributes : 보안 설정 값 일반적으로 0 이 들어갑니다.

- dwStackSize : 생성될 스레드가 얼마만큼의 스택 사이즈를 가질것인지를 의미함 0으로 설정하면 기본 1메가가 항당됩니다.

- lpStartAddress : 실제 호출할 쓰레드 함수명입니다.

- lpParameter : 쓰레드로 넘겨지는 파라미터 정보입니다.

- dwCreationFlags : 쓰레드 생성 플래그 정보입니다. 0 이면 즉시 실행 CREATE_SUSPENDED 옵션이면 생성은 되지만 SUSPENDED 상태 즉  멈춰있게 됩니다.

- lpThreadId : 스레드의 고유 번호를 나타냅니다.

 

실제 사용 예제입니다.

HANDLE m_Thread;
DWORD dwThreadID;
DWORD dwExitCode;

 

DWORD WINAPI ThreadFunction( PVOID pData ) //실제 쓰레드 함수
{
 
 TRACE(_T("this is thread function : create thread"));

 return true;
}

void OnBnClickedThread1() //쓰레드 함수 호출 부분

 m_Thread  = CreateThread(NULL, 0, ThreadFunction, _T("") ,0, &dwThreadID);
 
 GetExitCodeThread(m_Thread, &dwExitCode);

 while ( dwExitCode== STILL_ACTIVE)
 {
   Sleep(100);
   GetExitCodeThread(m_Thread, &dwExitCode);
 }


}

쓰레드의 종료 여부를 판단하기 위해 GetExitCodeThread 를 쓰게되면 무한 루프를 돌면서

쓰레드가 종료되기를 기다릴수 밖에 없습니다.

 

따라서 GetExitCodeThread 보다는 WaitForSingleObject 를 주로 많이 사용합니다.

 

아래의 수정된 예를 보시기 바랍니다.

 

void OnBnClickedThread1() //쓰레드 함수 호출 부분

 m_Thread  = CreateThread(NULL, 0, ThreadFunction, _T("") ,0, &dwThreadID);
 WaitForSingleObject(m_Thread, INFINITE);

}

 

WaitForSingleObject 함수의 두번째 인자로 대기시간을 설정할수 있습니다.

위의 예제는 무한대로 기다리는 코드이지만 밀리세컨트 단위로 대기시간을 설정할 수 있습니다.

 

만약 대기시간이 경과되었을 경우에 강제로 쓰레드를 종료하는 방법으로는 TerminateThread 가 있습니다.

하지만 TerminateThread 를 사용하게 되면 쓰레드는 종료될지라도 메모리 leak 이 발생하기 때문에

가급적이면 사용을 하지 마시고 , 정상적으로 쓰레드를 종료하는 것이 바람직합니다.

 

TerminateThread 를 사용한 예제입니다.

 

DWORD WINAPI ThreadFunction( PVOID pData )
{  
 for (int i=0; i< 100000; i++)
 {
  TRACE(_T("this is thread function : create thread\n"));
 }

 return true;
}

 

void OnBnClickedThread1()

 m_Thread  = CreateThread(NULL, 0, ThreadFunction, _T("") ,0, &dwThreadID);
 
 GetExitCodeThread(m_Thread, &dwExitCode);

 WaitForSingleObject(m_Thread, 10); //정확하게 쓰레드의 종료 여부를 확인하지 않고 10밀리 세컨드만 기다림

 TerminateThread(m_Thread,dwExitCode); //강제로 쓰레드를 종료함

 
}

 

*쓰레드를 즉시 시작하지 않고 생성후 대기 상태가 있다가 시작하려면 아래와 같이 호출을 합니다.

m_Thread  = CreateThread(NULL, 0, ThreadFunction, _T("") ,CREATE_SUSPENDED, &dwThreadID);

ResumeThread(m_Thread);

 

CreateThread 함수는 기본적으로 단일 파라미터가 넘어가지만 (또는 NULL 파라미터) 여러개의

값을 넘기면 싶으면 구조체를 활용하면 됩니다.

 

typedef struct
{
 int a;
 int b;
}ThreadStruct;


DWORD WINAPI ThreadFunction( PVOID pData )

 ThreadStruct *pParam  = (ThreadStruct*)pData;

 CString strTmp;
 strTmp.Format(_T("a=%d, b=%d"),pParam->a, pParam->b);
 
 TRACE(strTmp);

 return true;
}


void OnBnClickedThread1()

 ThreadStruct m_ThreadParam;
 
 m_ThreadParam.a = 100;
 m_ThreadParam.b = 200;

 

//구조체 정보를 파라미터로 넘김

 m_Thread  = CreateThread(NULL, 0, ThreadFunction, (LPVOID)&m_ThreadParam ,0, &dwThreadID);
 WaitForSingleObject(m_Thread, INFINITE);

}

 

2> _beginthread, _beginthreadex

c/c++ 런타임 라이브러리안에 있는 _beginthread 또는 _beginthreadex 를 이용하여 쓰레드를 생성할 수 있습니다.

 

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 
);

_beginthread , _beginthreadex 를 사용하기 위해서는 헤더에 #process.h 를 include 해야 합니다.

아래의 예제를 보시기 바랍니다.

 

*_beginthread 사용 예제 

void ThreadFunction( PVOID pData )

 
 TRACE(_T("this is _beginthread Exam\n"));
 
 int h=*((int*)pData);
  
 CString strTmp;
 strTmp.Format(_T("h= %d"),h);
 TRACE(strTmp);

 return;
}


void OnBnClickedThread1()

 
 int val=10;
 m_Thread =  (HANDLE)_beginthread(ThreadFunction, 0 ,&val);
 }

 

_beginthread 는 파라미터가 3개밖에 되지 않아 제어할수 있는 인자가 부족합니다.

좀더 원할한 쓰레드의 제어를 위해 대부분 _beginthreadex 를 더 많이 사용합니다.

 

아래의 예제를 보겠습니다.

 

*_beginthreadex 사용 예제

unsigned int WINAPI ThreadFunction( PVOID pData )

 
 TRACE(_T("this is _beginthreadex  Exam\n"));
 
 int h=*((int*)pData);
  
 CString strTmp;
 strTmp.Format(_T("h= %d"),h);
 TRACE(strTmp);

 return true;
}

 

void OnBnClickedThread1()

 
 int val=10;
 m_Thread =  (HANDLE)_beginthreadex(NULL,NULL,ThreadFunction,&val,0,(unsigned int *)dwThreadID);
 
 WaitForSingleObject(m_Thread, INFINITE);
}

 

절대 쓰레드 내부에서 _endthread() 를 호출하지 않습니다.

메모리 릭이 발생합니다.

함수가 정상 종료되도록 로직을 구현하여야 합니다.


 

 

 

_beginthread 와 beginthread  의 차이점에 대해 자세한 사항을 알고 싶으신 분들은

아래 링크를 참조 하시기 바랍니다.

 

원본주소 : 

http://blog.naver.com/drags99/150032023121 : 애룡이다 님의 글입니다.

 

두 함수는 모두 스래드를 생성할 때 호출하는 함수이다.

이 두 함수는 겉으로 보기엔 동일한 기능을 하지만 차이점이 있어서 이를 알고 넘어가야 한다.

MSDN 보면 _beginthread  _beginthreadex  사용하면 스래드에 여러 인수를 전달할  있습니다라고 되어있다이러한 이유로 CreateThread 보다는   함수를 많이 이용한다.

위 두 함수 중에도 개인적으로는 _beginthreadex 함수를 주로 이용하고 있다.

위 두 함수의 차이점을 비교해 보면

함수 호출 규약이 _beginthread 함수는 __cdecl 이고, _beginthreadex 함수는 __stdcall 이다.

함수 호출 규약에 따라 내부적으로 인자 값들을 스택에 저장할 때 순서가 달라진다이러한 부분은 코딩을 하는 부분에서는 크게 신경 쓰지 않아도 되는 부분이다.

 

또 다른 차이점은 각각의 함수는 _endthread, __endthreadex 라는 함수와 같이 이용된다는 점이다 봐도 뒤에 ex  붙은 함수와  붙은 함수가 세트로 사용된다는  쉽게 이해할  있을 것이다.

 

또 다른 차이점은 return Value 값이다.

_beginthread 함수의 경우 성공일  0, 실패일  -1 리턴 하지만, _beginthreadex 함수는실패일  0(NULL), 성공일  -1 아닌 값을 리턴 한다.

_beginthread 함수는 성공과 실패를 구분하지만 _beginthreadex 함수는 실제 HANDLE  리턴하기 때문에 GetExitCodeThread 함수 호출 등이 가능하게 된다.

두 함수를 호출하는데 있어서 인자 값도 차이가 난다.

_beginthreadex 함수에서 initflag 값을 이용해 스래드가 생성되는 시점에서 스래드의 상태를 설정할  있다.

threadaddr  32bit 포인터 값으로 스래드의 주소 값을 반환 받을  있다.

Security 인자를 이용해 자식 프로세스에서  함수에서 반환된 스래드의 핸들을 상속받아서 사용할  있는지 유무를 결정할  있다.

위와 같은 차이가 나지만 대부분은 _beginthreadex 함수를 많이 이용할 것이다.

 

하지만 _beginthreadex 함수를 이용해야 하는 아주 결정적인 이유가 하나  있다.

Ansi 표준 C 라이브러리 함수를 호출할 경우 _beginthread 함수를 이용해 생성된 스래드 에서는 동기화 이유 때문에 안정성을 보장받지 못하게 되지만 _beginthreadex 함수를 이용할 경우 스래드별로 내부적으로 별도의 메모리 공간을 할당하기 때문에 동기화와 관련돼 안정성을 보장받을  있게 된다이렇게 스래드 생성시 할당된 별도의 메모리 공간을 해제하기 위해서 _endthreadex 함수와 짝을 이뤄서이용하게 되는 것이다.

컴파일러에 따라 다르겠지만 .Net 에서는 멀티스래드에 안정적인 라이브러리를 제공하고 있기 때문에별도의 선언이나 조작 없이도 안정적인 라이브러리를 이용할  있다고 한다.(아직까지는  문제 없이 쓰고 있다.)

만약 _beginthreadex 함수로 스래드를 생성하고 _endthread 함수를 이용해 스래드를 종료하게 된다면스래드 생성시에 할당된 메모리가 반환되지 않기 때문에 이렇게 스래드를 이용할  없다.

하지만 스래드를 생성할 때 인자로 넘긴 함수 포인터에서 return  이용해 스래드를 종료하게 되면 별도의 _endthreadex 함수를 호출하지 않아도 생성시에 할당된 메모리가 반환되기 때문에 return  이용하면  부분은 별로 걱정할 필요 없다.

스래드 종료 시 반환된 값은 내부에 생성된 스래드 커널에 저장되고, GetExitCodeThread 함수를 이용해 받아올  있다.

하지만 위 함수를 호출하기 전에 CloseHandle 함수를 이용해 스래드 핸들을 종료시킨다면 내부에 생성된 스래드 커널이 삭제되기 때문에 반환 값을 받을 수 없게 된다.

스래드의 반환 값을 이용할 필요가 있을 경우 GetExitCodeThread 함수를 이용해서 반환 값을 받아온후에 CloseHandle 함수를 호출해 줘야 한다.

추가로 한가지 더 얘기 하면 CloseHandle  이용해서 함수 호출  반환된 스래드의 핸들을 종료시키게 되면 내부적으로 스래드 커널에 UC 줄어들게  스래드가 종료되는 시점에서 스래드 커널도 바로종료될  있다 부분은 커널과 핸들러에 관한 내용을 미리 알고 있어야 해서 간단히 설명한다.

 

3>AfxBeginThread

지금까지는 주로 WIN32 프로그래밍 시절에 사용했던 쓰레드에 대해 언급했습니다.

MFC 라이브러리를 사용하시는 분들은 AfxBeginThread 사용을 권장합니다.

원형은 아래와 같습니다.

CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

 

기본적으로NULL 로 세팅되는 파라미터가 많기 때문에 간단하게 두개의 파라미터로 쓰레드를

실행 시킬수 있습니다.

 

아래 예제를 보시기 바랍니다.

 

//쓰레드 함수

UINT ThreadFunction(LPVOID lParam) 

 
 TRACE(_T("this is afxbeginthread Exam\n"));
 
 return true;
}

 

CWinThread *m_MFCThread = NULL;

 

//쓰레드 호출 부분

void OnBnClickedThread1()

 m_MFCThread = AfxBeginThread(ThreadFunction, this); 
 m_MFCThread->m_bAutoDelete = FALSE;
 m_MFCThread->ResumeThread();

 

 if (m_MFCThread)
 { 
  WaitForSingleObject(m_MFCThread->m_hThread,INFINITE);
  delete m_MFCThread;
 }

}

 

* AfxBeginThread  사용시 중요한 점이 두가지가 있습니다.

초기에 CWinThread  의 m_bAutoDelete 값은 무조건 FALSE 로 설정을 해야 합니다.

그래야만 WaitForSingleObject 를 통해 m_MFCThread->m_hThread 의 리턴여부를 정확하게

받을 수 있습니다. TRUE 로 설정하면 WaitForSingleObject  에서 리턴이 되어 버립니다.

CWinThread 는 내부적으로 메모리를 할당하기 때문에 반드시 delete 를 해주어야 합니다.

안 그러면 메모리 릭이 발생합니다.

 

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://despolt.tistory.com/entry/beginthreadex-%EC%98%88%EC%A0%9C

 

 

#include <iostream>

#include <Windows.h>

#include <process.h>

 

int test_int = 0;

 

// 1번째 Thread

unsigned int WINAPI Thread_Function_one(void *avg)

{

std::cout << "(Before)Thread One Test Int : " << test_int << std::endl;

test_int++;

std::cout << "(After)Thread One Test Int : " << test_int << std::endl;

 

return test_int;

}

 

// 2번째 Thread

unsigned int WINAPI Thread_Function_two(void *avg)

{

std::cout << "(Before)Thread Two Test Int : " << test_int << std::endl;

test_int++;

std::cout << "(After)Thread Two Test Int : " << test_int << std::endl;

 

return test_int;

}

 

void main()

{

HANDLE thread_one;

HANDLE thread_two;

 

DWORD thread_one_id;

DWORD thread_two_id;

 

std::cout << "Main Test Int : " << test_int << std::endl;

 

  // Thread One 생성

thread_one = (HANDLE)_beginthreadex(NULL, 0, Thread_Function_one, NULL, 0, (unsigned*)&thread_one_id);

if(thread_one == 0)

{

std::cout << "_beginthreades Error" << std::endl;

}

else 

{

std::cout << "Create Thread One" << std::endl;

}

  

  // Thread Two 생성

thread_two = (HANDLE)_beginthreadex(NULL, 0, Thread_Function_two, NULL, 0, (unsigned*)&thread_two_id);

if(thread_two == 0)

{

std::cout << "_beginthreades Error" << std::endl;

}

else 

{

std::cout << "Create Thread Two" << std::endl;

}

 

Sleep(3000);

std::cout << "Main Test Int : " << test_int << std::endl;

std::cout << "Main Exit" << std::endl;

}

 

쓰레드 두 개를 만들고 생성한 뒤

전역 변수에 있는 test_int 를 각 쓰레드에서 더할 경우 값은 둘 다 바뀌게 된다.

 

전역에 있는 변수들은 각 쓰레드에서도 같은 위치의 값들을 변경하기 때문에

before에서 0이 되더라도 더하게 되면 2가 되어 출력된다.



출처: http://despolt.tistory.com/entry/beginthreadex-예제 [Andras Town]

[출처] 스물네번째-쓰레드의 모든것(C++,MFC)|작성자 모노

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://killsia.tistory.com/entry/CreateThread-beginthread-beginthreadex%EC%9D%98-%EC%B0%A8%EC%9D%B4

 

CreateThread

 

1. CreateThread는 c/c++ Runtime Library에서 제공하는 함수가 아닌, WinApi 전용함수이다. 때문에 return값이 
HANDLE 타입으로 반환된다. 
 
2. Thread를 사용할일이 있을때 CreateThread함수를 쓰면 안된다. 왜냐하면, CreateThread를 이용하여 생성한 스레드 구문 내부에 c/c++ 런타임 함수를 사용할경우, c/c++함수는 실행되지 않고 스레드만 죽어버리기 때문이다.
예를들어 strtok함수의 경우 내부적으로 처음 호출때 받은 포인터를 내부적으로 정적변수로 저장하고 이후 호출에서 사용한다. 
이럴 경우 다른 쓰레드에서 이 함수를 호출하게 되면 처음 호출때 받은 내부 정적 변수를 덮어 써 버리게 된다. 
이런 문제를 피하기 위해서 윈도우즈에서는 TLS(Thread Local Storage)를 제공한다.
 
3. 왠만하면 쓰레드 생성시에 CreateThread는 안쓰는게 좋다

 

_beginthread

 

 내부적으로는 CreateThread()사용하여 쓰레드를 생성하지만, C Runtime Library 내부적으로 필요로 하는 메모리 영역을 초기화 해주는 역할도 한다. 초기화 되는 메모리 영역은 각 쓰레드 마다 따로 관리된다.
_beginthread : 
1. 보안특성을 가진 쓰레드를 생성할수 없다.
 
2. 일시 정지된 상태의 쓰레드도 생성할수 없다.
 
3. 스레드 종료시에 CloseHandle()이 내부적으로 호출되므로, 쓰레드 함수 완료후 쓰레드의 ID값도 얻을수 없기 때문에 쓰레드 종료후에
쓰레드 오브젝트와 통신할수 없다

 

_beginthreadex

 

1. 내부적으로 메모리 블럭을 할당한다.
2. 예외처리 프레임을 구성하고 signal함수가 정상동작하도록 작업을 수행한다.
3. 쓰레드가 실행완료된후 내부적으로 CloseHandle()을 호출하지 않기 때문에 사용자가 명시적으로 CloseHandle()을 호출해주어야 한다.
4. 내부적으로 _exitThreadex()함수를 호출하여 메모리 정리까지 해준다.(멀티 쓰레드 환경에 적합하다)
5. 또한 return값이 unsigned int형이므로 (HANDLE)형으로 캐스팅해주면 Win32에서 지원하는 Thread관련 함수들도 사용할수 있다.

[출처] CreateThrea, _beginThread, _beginThreadex 비교 분석|작성자 superchangho



출처: http://killsia.tistory.com/entry/CreateThread-beginthread-beginthreadex의-차이 [One Day One Line]

 

 

 

=======================

=======================

=======================

 

 

반응형