HANDLE h_thread = (HANDLE)_beginthreadex(
NULL, 0, // winsock: NULL, 0
count_thread, (void*)¶meter, // 스레드 함수, 파라미터
0, &thread_id // initflag, 스레드 ID
);
WaitForSingleObject(h_thread, INFINITE);
WaitForMultipleObjects(
NUM_THREAD, hThreads, // 오브젝트 수, 오브젝트 배열
TRUE, INFINITE // 전부, timeout
);1. 커널 오브젝트
-
커널 오브젝트 (Kernel Objects)
- 시스템 리소스를 관리하기 위해 운영체제가 만드는 데이터 블록
-
운영체제가 만들고 관리하는 리소스
- 프로그램 실행과 관련된 프로세스·쓰레드
- 입출력 도구인 소켓·파일
- 쓰레드 간 동기화에 쓰이는 세마포어·뮤텍스
-
핵심
- 커널 오브젝트의 소유자가 운영체제
- 생성·관리·소멸이 모두 운영체제의 몫
- 리소스 종류에 따라 커널 오브젝트의 형태도 달라진다.
1.1. 커널 오브젝트의 상태
커널 오브젝트는 두 가지 상태를 가진다.
-
non-signaled 상태
- 이벤트가 아직 발생하지 않은(특정 상황에 이르지 않은) 상태
-
signaled 상태
- 이벤트가 발생한(특정 상황에 도달한) 상태
- 예를 들어
- 프로세스나 쓰레드의 커널 오브젝트는 생성 시 on-signaled 상태였다가 종료 시 signaled 상태로 바뀐다.
- 이 상태 정보를 통해 우리는 리소스의 상황을 인식할 수 있다.
1.2. 커널 오브젝트의 모드
-
auto-reset 모드
- Wait 계열 함수가 반환되면서 자동으로 다시 non-signaled 상태로 돌아오는 커널 오브젝트
-
manual-reset 모드
- 그렇지 않은 커널 오브젝트
1.3. 커널 오브젝트의 상태 확인
(1) WaitForSingleObject()
전달된 핸들의 커널 오브젝트가 signaled 상태가 되어야 함수가 반환된다.
DWORD WaitForSingleObject(
HANDLE hHandle, // h_thread
DWORD dwMilliseconds // INFINITE -> signaled가 될 때까지 무한 대기
);- 반환
WAIT_OBJECT_0: signaledWAIT_TIMEOUT: 타임아웃
(2) WaitForMultipleObjects()
여러 커널 오브젝트를 한 번에 관찰
DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
// TRUE면 모든 오브젝트가 signaled가 되어야 반환
// FALSE면 하나라도 signaled가 되면 반환
DWORD dwMilliseconds
);2. 프로세스와 스레드
- 프로세스와 스레드의 관계
- 현대 운영체제는 쓰레드를 OS 레벨에서 지원하므로
main함수 호출조차도 하나의 쓰레드가 수행한다. - 프로세스는 쓰레드를 담는 상자이다.
- 추가 쓰레드를 만들지 않으면 단일 쓰레드 모델, 추가로 만들면 멀티 쓰레드 모델 프로그램이 된다.
- 현대 운영체제는 쓰레드를 OS 레벨에서 지원하므로
3. 윈도우 기반의 쓰레드 생성
방법 1. CreateThread()
- 쓰레드 생성 함수
#include <windows.h>
HANDLE CreateThread( // 성공 시 쓰레드 핸들, 실패 시 NULL이 반환
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress, // ** 쓰레드가 실행할 main 함수 **
LPVOID lpParameter, // ** main 함수에 전달할 인자 **
DWORD dwCreationFlags,
LPDWORD lpThreadId
);- 환경설정 주의사항
- 멀티 쓰레드 프로그램을 작성할 때는
- 프로젝트 속성에서 런타임 라이브러리를 “다중 스레드(/MT)” 또는 “다중 스레드 디버그(/MTd)” 등
- 멀티 쓰레드용으로 지정해야 C/C++ 표준 함수가 안정적으로 호출된다.
방법 2. _beginthreadex() (권장)
_beginthreadex()- C 표준 함수의 안전한 호출
- 매개변수의 의미와 순서는
CreateThread와 동일하다. - 이 함수로 생성된 쓰레드는 표준 C/C++ 함수에 대해 안정적으로 동작한다.
- 따라서 표준 C 함수를 호출하는 경우에는 반드시
_beginthreadex를 써야한다.
#include <process.h>
uintptr_t _beginthreadex(
void *security, // 보통 기본값인 NULL을 주로 전달
unsigned stack_size, // 0을 전달하면 기본 스택 크기를 사용
unsigned (*start_address)(void *), // ** 새 스레드가 실행할 함수의 포인터 **
void *arglist, // ** 스레드 함수가 실행될 때 인자로 넘겨줄 데이터의 포인터 **
unsigned initflag, // 스레드의 초기 생성 상태를 제어하는 플래그
unsigned *thrdaddr // 생성된 Thread ID 값을 저장할 변수의 주소
);-
cf.
CreateThread()는 단순히 OS 레벨 스레드만 생성한다.- CRT(C 런타임 라이브러리)는 새 스레드가 생긴 걸 모른다.
- 그래서 CRT가 필요한 내부 구조를 초기화하지 않는다.
-
리눅스와 달리 윈도우의 쓰레드는 쓰레드 함수가 반환되면 자동으로 소멸된다.