Windows_/Windows_Source

Windows :: 스레드 생성 함수와 예제 // CreateThread _beginthreadex

sosal 2014. 7. 24. 10:17
반응형

/*

 * http://sosal.kr/

 * made by so_Sal

 */

 

 


스레드 (혹은 쓰레드, Thread) 에 대해서 잘 모르신다면
링크로~! ^-^ :: LINK_

/*
 * CreateThread()
 * ExitThread()
 * GetExitCodeThread()

 * _beginthreadex()
 *  ResumeThread();
 * _endthreadex()
 *  -------------
 */
Windows에서 쓰레드를 생성하는 가장 기본적인 함수는
CreateThread 입니다.

HANDLE CreateThread(
1.        LPSECURITY_ATTRIBUTES lpThreadAttributes,
2.        SIZE_T dwStackSize,
3.        LPTHREAD_START_ROUTINE lpStartAddress,
4.        LPVOID lpParameter,
5.        DWORD dwCreationFlags,
6.        LPDWORD lpThreadId        );

1. LPSECURITY_ATTRIBUTES lpThreadAttributes,
SECURITY_ATTRIBUTES 구조체는,
생성하는 핸들에 대해 보안속성을

2. SIZE_T dwStackSize //dw는 DWORD겠죠 ㅎㅎ
쓰래드는 고유의 스택을 가지고 있습니다. 스택 크기를 지정합니다.
0 (또는 NULL) :: Default 값으로 1mb가 적용됩니다.

3. LPTHREAD_START_ROUTINE lpStartAddress

쓰레드가 작동할 함수의 이름을 넣으시면 됩니다.
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)
                  (LPVOID lpThreadParameter);
typedef PTHREAD_START_ROUTINE    LPTHREAD_START_ROUTINE;

함수 예)
DWORD WINAPI ThreadEx(LPVOID lpParameter){
        return 0;
}

4. LPVOID lpParameter
함수의 인자로 넘어가는것과 같습니다.
더블 포인터도 가능합니다.

5. DWORD dwCreationFlags, //Flag 입니다.
CREATE_SUSPEND
    
:: suspend count 1로 설정 ( 스레드 priority control 관련글 참고 링크 :: LINK_ )
     :: suspend count가 0이 되기 전까지는, 스레드는 동작하지 않습니다.
        이 인자를 넣을 시에, 원하는 시기에 스레드를 시작할 수 있습니다.
        DWORD ResumeThread(HANDLE hThread)   ::  Suspend Count 1 감소
        DWORD SuspendThread(HANDLE hThread)   ::  Suspend Count 1 증가

STACK_SIZE_PARAM_IS_A_RESERVATION


     :: Reserve stack size를 변경하려면 위 플레그를 추가 한 후 
        스레드 생성 함수들의 매개변수 dwStackSize파라미터를 사용한다.

아래는 CreationFlag이지만, 프로세스에서만 쓰인다. 
CREATE_NEW_CONSOLE
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS

6. LPDWORD lpThreadId
생성시에 이 변수로 쓰레드ID가 전달됩니다.
필요 없다면 NULL.

return :: HANDLE.
CreateThread 함수의 리턴값은, 스레드를 가리키는 핸들

스레드는 독립된 스택을 할당받기 때문에 메모리를 차지하게 됩니다.
메모리가 허용하는 만큼 스레드 생성이 가능합니다.







void
 ExitThread(DWORD dwExitCode)
이 함수는 실행중인 스레드를 종료하고자 할 때 호출하는 함수입니다.
(사실 return과 다를 바 없다.)
return으로 종료되는 스레드나, 이 함수로 종료되는 스레드나
모두 아래에 설명할 GetExitCodeThread 함수로 리턴값 (혹은 ExitCode값)을
반환받을 수 있다.







스레드의 리턴값을 가져오는 함수입니다.
BOOL GetExitCodeThread(
1        HANDLE hThread,
2        LPDWORD lpExitCode );

1. HANDLE :: 정상적인 리턴으로 종료된 스레드를 가리키는 핸들값
2. lpExitCode :: DWORD 자료형 변수의 주소값을 넣어주면
                       스레드의 리턴값 DWORD값을 반환

다중 프로세스의 경우에, GetExitCodeProcess() 함수 또한,
위와 같이 사용할 수 있습니다. (자료형, 매개변수가 같습니다.)





#include<process.h> 헤더파일이 필요합니다.
uintptr_t _beginthreadex(
          void *_Security,
          unsigned int _StackSize,
          unsigned int(*_StartAdrdress)(void *),
          void *_ArgList,
          unsigned int _InitFlag,
          unsigned int *_ThrdAddr    );

매개변수는 CreateThread와 일치합니다.
매개변수들의 자료형을 더욱 범용적으로 쓰기 위해서
자료형의 형태가 수정되었습니다.

CreateThread() 와 위 함수의 가장 큰 차이점은,
독립적인 메모리 블록 할당에 있습니다.
_beginthreadex() 함수 역시 내부적으로 CreateThread()를 호출합니다.
종료호출로 ExitThread() 대신, 아래 함수로 대신합니다.
(ExitThread() 함수를 쓰는 이유는 독립적인 "메모리 블록 할당" 에 있습니다.)



void _endthreadex(unsigned retval);
ExitThread() 함수와 역시 동일합니다. 하지만,
_endthreadex() 함수를 쓰는 이유는,
CreateThread()와 _beginthreadex() 함수간의 차이에 있습니다.
_beginthread() 함수에서는 독립적인 메모리 블록 할당을 한다고 하였습니다.
따라서 스레드 종료시에, 할당한 메모리를 반환해야만 합니다.
이 역할을 하는 함수가 _endthreadex() 입니다.
















======================== CreateThread() 사용 함수 예제 ========================


/*
 * CraeteThread()함수를 이용하여 스레드를 생성한 후
 * 각각의 스레드에 매개변수로 1~10을 넘깁니다.
 * 스레드는 매개변수로 받은 숫자 1~10을 전역변수 STotal 변수에 +=으로 더하고,
 * 매개변수값을 리턴합니다.
 * 메인루틴에서는 스레드가 리턴한값을 차례대로 GetExitCodeThread() 함수를 통해
 * 반환하여 모두 더하고, 전역변수와 리턴값의 합을 출력합니다.
 * ExitThread() 함수는 사용하지 않고, return을 이용했습니다. 
 * ExitThread() 함수를 이용해 직접 짜보시면 이해에 더욱 도움이 될 것 같습니다.
 */


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

#define MAX_THREADS (10)

DWORD STotal = 0;
DWORD WINAPI ThreadProc(LPVOID lpParam);
        // 생성될 Thread가 수행할 내용이 담긴 함수

int _tmain(int argc,TCHAR *argv[]){

 DWORD cntOfThread=0;
 DWORD dwThreadID[MAX_THREADS];
 HANDLE hThread[MAX_THREADS];

 DWORD Total =0;
 DWORD Result=0;
 

while(1){
    if(cntOfThread == MAX_THREADS){
      _tprintf( _T("MAXIMUM THREAD NUMBER : %d\n") ,cntOfThread);
       break;
    }
    hThread[cntOfThread] =
     CreateThread(
         NULL,0,ThreadProc,
         (LPVOID)cntOfThread,
         0,
         &dwThreadID[cntOfThread]);
         cntOfThread++;
}  //while문을 이용하여 10개의 스레드 생성

 Sleep(1000);
 // 스레드가 자신의 할일을 모두 수행하고, 리턴값을 남기고 사라짐
 // 리턴값을 이용하여 Total 계산

 for(DWORD i=0; i<cntOfThread; i++){
  GetExitCodeThread(hThread[i],&Result);
  Total += Result;
  CloseHandle(hThread[i]);
 }
 
 _tprintf( _T(" Total :: %d \n"), Total);      //스레드의 리턴값으로 얻은 Total
 _tprintf( _T("STotal :: %d \n"), STotal);   //스레드가 전역변수에 접근하여 계산된 값
 return 0;
}

DWORD WINAPI ThreadProc(LPVOID lpParam){
   STotal += (DWORD)lpParam; // 전역변수도 접근 가능
   return (DWORD)lpParam;      // 리턴값       전달 가능
}





======================== _beginthreadex() 사용 함수 예제 ========================
/*
 * 위 CreateThread() 예제와
 * 같은 일을 수행합니다.
 */



#include<stdio.h>
#include<windows.h>
#include<tchar.h>
#include<process.h> //beginthreadex() 함수 사용시 필요 헤더파일

#define
MAX_THREADS (10)

DWORD STotal = 0;
unsigned int WINAPI ThreadProc(LPVOID lpParam);
        // 생성될 Thread가 수행할 내용이 담긴 함수.
        // CreateThread와는 다르게 unsigned int 자료형을 사용합니다.

int _tmain(int argc,TCHAR *argv[]){

 DWORD cntOfThread=0;
 DWORD dwThreadID[MAX_THREADS];
 HANDLE hThread[MAX_THREADS];

 DWORD Total =0;
 DWORD Result=0;
 

while(1){
    if(cntOfThread == MAX_THREADS){
      _tprintf( _T("MAXIMUM THREAD NUMBER : %d\n") ,cntOfThread);
       break;
    }
    hThread[cntOfThread] =
     (HANDLE) _beginthreadex(       //HANDLE로의 형변환이 필요합니다.
         NULL,0,ThreadProc,
         (LPVOID)cntOfThread,
         0,
         (unsigned *)&dwThreadID[cntOfThread]); 
         cntOfThread++;
}  //while문을 이용하여 10개의 스레드 생성

 Sleep(1000);
 // 스레드가 자신의 할일을 모두 수행하고, 리턴값을 남기고 사라짐
 // 리턴값을 이용하여 Total 계산

 for(DWORD i=0; i<cntOfThread; i++){
  GetExitCodeThread(hThread[i],&Result);
  Total += Result;
  CloseHandle(hThread[i]);
 }
 
 _tprintf( _T(" Total :: %d \n"), Total);      //스레드의 리턴값으로 얻은 Total
 _tprintf( _T("STotal :: %d \n"), STotal);   //스레드가 전역변수에 접근하여 계산된 값
 return 0;
}

unsigned int WINAPI ThreadProc(LPVOID lpParam){ //unsigned int 자료형
   STotal += (DWORD)lpParam; // 전역변수도 접근 가능
   return (DWORD)lpParam;      // 리턴값       전달 가능
}