윈도우 기반 쓰레드  CreateThread, _beginthredex

쓰레드 관련 정보를 찾다가 잘 정리된 포스팅을 발견하고 정리를 했습니다. 

 포스팅 원작자는 해당 포스팅을 비공개 해놓은 상태이고 내용은 여기 저기 블로그에 복사된 상태입니다. 


그래서 저도... 슬쩍 복사했습니다. (양심 상 오타 및 요약 했습니다. 에버노트 저장용으로 할라다가 블로그에도 올립니다.)


출처 : 어른아이  (감사합니다.!!)






윈도우즈 쓰레드란 윈도우즈 기본적인 프로그램 실행 단위.

프로그램이 실행이 되면 프로세스가 실행이 된다. 프로스세스 내부에 메인 쓰레드가 생성이된다.
메인 쓰레드가 main함수를 실행한다.

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

윈도우 쓰레드 생성 함수

#include <windows.h>
HANDLE CreateThread(
 LPSECURITY_ATTRIBUTES lpThreadAttributes, // Security Descriptor
 SIZE_T dwStackSize, // initial stack size
 LPTHREAD_START_ROUTINE lpStartAddress, // thread function
 LPVOID lpParameter, // thread argument
 DWORD dwCreateionFlags, // thread identifier
 LPDWORD lpThreadId // thread identifier
);

리턴 값 : 성공 시 생성된 커널오브젝트의 핸들, 실패 시 NULL 리턴

인자 값 :

○  lpThreadAttributes
 생성하려는 쓰레드의 보안에 관련된 설정
 디폴트(Default) NULL

○ dwStackSize
 쓰레드를 생성하는 경우, 모든 메모리 공간은 스택 공간은 독립적으로 생성된다.
 쓰레드 생성 시 요구되는 스택의 크기
0을 전달할 경우 디폴트로 설정되어 있는 스택 크기를 할당

○ lpStartAddress
 쓰레드에 의해 호출되는 함수의 포인터

○ lpParameter
 lpStartAddress 가 가리키는 함수 호출 시  전달할 인자

○ dwCreateionFlags
 새로운 쓰레드 생성 이후에 바로 실행 가능한 상태가 되느냐, 아니면 대기 상태로 들어가느냐를
 결정하는 요소이다.
 중요한 요소는 아니며 0을 전달할 경우 바로 실행 가능한 상태가 된다.

○ lpThreadId
 쓰레드 생성 시 쓰레드의 ID가 리턴되는데, 이를 저장하기 위한 변수의 포인터이다.

-->중요한 인자는 lpStartAddress , lpParameter , 나머지는 디폴드 값 혹은 0, 또는 NULL 사용

쓰레드 소멸 시점
1. 쓰레드가 소멸되는 시점은 쓰레드에 의해 호출된 함수가 리턴하는 시점이다. 함수가 종료되면서 임의의 상수를 리턴하면서 쓰레드는 소멸하게 된다.

2. ExitThread함수를 사용 할경우 리소스를 해제하는 과정을 반드시 거쳐야함. (불편함)

일반적으로 함수 리턴을 통한 종료를 선호 (호환성 문제 때문에)

CreateThread <- 안전하지 않은 쓰레드로 가급적 사용 X

멀티 쓰레드 기반의 프로그램 작성시 환경 설정 방법
 - 비주얼 스튜디오 기준 Alt+F7 프로젝트 설정에서 런타임 라이브러리(User run-time library)목록에서 멀티스레드 DLL옵션을 줘야함.

안전한 쓰레드 C 라이브러리 함수 사용하기

 C 라이브러리 개발 당시 멀티 쓰레드에 대한 고려가 되어 있지 않아, 표준 C라이브러리 함수 중 일부는 멀티 쓰레드 프로그램 내에서 안전하지 않다.
이러한 문제를 해결하기 위해 안전하 쓰레드 c/c++ 라이브러리를 제공한다.

_beginthreadex 함수

#include <process.h>
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,  // createion option

  unsigned* thrdaddr  // thread identifier
  )

리턴 값 : 성공 시 생성된 소켓의 핸들, 실패 시 0 리턴

인자 : CreateThread 함수와 동일...

○  security
 생성하려는 쓰레드의 보안에 관련된 설정
 디폴트(Default) NULL 포인터를 전달한다.

○ stack_size

 쓰레드를 생성하는 경우, 모든 메모리 공간은 스택 공간은 독립적으로 생성된다.

 따라서 쓰레드, 생성 시 요구되는 스택의 크기를 인자로 전달한다. 0을 전달할 경우
 디폴트로 설정되어 있는 스택  크기를 할당 받는다.

○ start_address
 쓰레드에 의해 호출되는 함수의 포인터를 인자로 전달한다.

○ arglist
 lpStartAddress 가 가리키는 함수 호출 시, 전달할 인자를 지정해 준다.

○ initflag

 새로운 쓰레드 생성 이후에 바로 실행 가능한 상태가 되느냐, 아니면 대기 상태로 들어가느냐를
 결정하는 요소이다.
 0을 전달할 경우 바로 실행 가능한 상태가 된다.

○ thrdaddr
 쓰레드 생성 시 쓰레드의 ID가 리턴되는데, 이를 저장하기 위한 변수의 포인터이다.

CreateThread() 비교하면 똑같음.

_beginthreadex 예제 샘플

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

#include <process.h>
unsigned int WINAPI ThreadFunction(void *arg);
void Error(const char *mes);
int main()

{

 HANDLE hThread = NULL;

 DWORD dwThreadID = NULL;
 hThread = (HANDLE)_beginthreadex(NULL,0,ThreadFunction,NULL,0,(unsigned*)&dwThreadID);
 if(hThread == 0) Error("_beginthreadex Error\n");
 printf("생성된 쓰레드의 핸들 : %d\n",hThread);

 printf("생성된 쓰레드의 ID : %d\n",dwThreadID);
 Sleep(3000);

 printf("main 함수 종료!!\n");
 return 0;

}

unsigned int WINAPI ThreadFunction(void *arg)

{

 for(int i=0; i<5; i++)

 {

  Sleep(2000);

  printf("쓰레드 실행 중\n");

 }
 return 0;

}
void Error(const char *mes)

{

 printf("%s\n",mes);

 exit(0);
}

메인쓰레드가 먼저 종료됨. 그래서 쓰레드에 "쓰레드 실행중" 5번 출력이 안됨. main() 함수에 무한 루프를 돌리면 5번 출력이되지만.. 프로그램 종료는 안됨. 

끝. 다음 글도 보고 싶지만 현재는 모두 비공개 된 상태입니다. (출처:어른아이)