https://basaeng.tistory.com/46
[뇌를 자극하는 윈도우즈 시스템 프로그래밍] 11장 쓰레드의 이해
개요하나의 프로그램 내에서도 여러가지의 실행 흐름이 필요할 때가 많다.특히 복잡한 프로그램 (예를 들어 게임)에는 프로그램 내에서 실행 흐름 다양화를 위해 전통적으로 프로세스를 여러개
basaeng.tistory.com
개요
이전 내용에 이어 이번에는 Thread의 생성, 소멸 방법에 대해 간단하게 알아보자.
Thread 생성
Process생성을 위해 CreateProcess를 사용했듯이, Thread생성을 위해서는 CreateThread를 사용한다.
CreateThread 함수(processthreadsapi.h) - Win32 apps
호출 프로세스의 가상 주소 공간 내에서 실행할 스레드를 만듭니다.
learn.microsoft.com
HANDLE CreateThread(
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in, optional] __drv_aliasesMem LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out, optional] LPDWORD lpThreadId
);
lpThreadAttributes는 이전의 Process 처럼 LPSECURITY_ATTRIBUTES구조체의 보안 설명자를 지정할 수 있는 인자이다.
dwStackSize는 생성되는 Thread의 stack 크기를 지정할 수 있다. NULL(0)으로 지정하면 기본 스택 크기는 1MB가 된다.
lpStartAddress는 해당 Thread가 시작할 함수의 포인터이다.
https://learn.microsoft.com/ko-kr/previous-versions/windows/desktop/legacy/ms686736(v=vs.85)
ThreadProc callback function (Windows)
Ask Learn Ask Learn Language Table of contents Read in English Add Add to plan Share via Facebook x.com LinkedIn Email Print Note Access to this page requires authorization. You can try signing in or changing directories. Access to this page requires autho
learn.microsoft.com
이 때 해당 함수는 ThreadProc의 형태로 정의되어야 한다.
lpParameter: 쓰레드 함수에 전달할 인자를 지정한다. main의 argv와 비슷하다.
dwCreationFlags: Thread의 생성, 실행을 조절하는 인자이다.
| CREATE_SUSPENDED0x00000004 | 스레드는 일시 중단된 상태로 만들어지고 ResumeThread 함수가 호출될 때까지 실행되지 않는다. (Blocked 상태) |
| STACK_SIZE_PARAM_IS_A_RESERVATION0x00010000 | dwStackSize 매개 변수를 지정하면 dwStackSize 값은 스택의 초기 reserve 크기를 지정하는 의미가 된다. 만약 이 플래그를 지정하지 않으면 dwStackSize는 commit 크기이다. |
lpThreadId: Thread ID를 전달받기 위한 변수 주소값을 전달한다.
Thread 소멸
Thread를 소멸하기 위한 여러 방법이 있다.
결론은 Thread는 return으로 종료하는 것이 이상적이고 이 경우만을 사용해야 하지만 간단하게 소개한다.
return 사용
Thread의 시작점으로 사용된 함수가 return하면 마치 main을 종료한 process 처럼 정상적으로 종료된다.
Thread의 스택도 정상적으로 정리되기 때문에, 소프트웨어적으로도 가장 정상적인 흐름이다.
ExitThread 함수 호출을 통해, 종료
ExitThread 함수(processthreadsapi.h) - Win32 apps
호출 스레드를 종료합니다.
learn.microsoft.com
ExitThread는 현재 진행 중인 Thread 내부에서 즉시 종료한다.
process에서 exit하는 것과 비슷하다. 다만 Thread 내부에서 다른 함수를 호출해 스택 프레임이 올라간 경우, 하위 스택 프레임을 정리하지 못한다는 점을 알아야 한다.
TerminateThread 함수 호출을 통해 종료
TerminateThread 함수(processthreadsapi.h) - Win32 apps
스레드를 종료합니다.
learn.microsoft.com
TerminateThread는 종료할 thread의 핸들을 인자로 사용해, 프로세스 다른 곳에서 종료할 수 있다.
외부에서 Thread를 강제종료시키는 것이기 때문에 매우 위험한 처리이다.
결론적으로 return을 통해 종료하는 것이 가장 적합하다.
Thread의 성격과 특성
Thread는 Stack과 Register Set은 따로 가지지만, Heap, Data영역 들은 같은 Process의 다른 Thread들과 공유한다.
따라서 Thread간 공유하는 데이터를 접근할 때 주의 할 점이 있다.
static int total = 0;
DWORD WINAPI ThreadProcAdd(LPVOID lpParam)
{
for (DWORD i = 0; i <= 100000; ++i)
{
++total;
}
return 0;
}
DWORD WINAPI ThreadProcSub(LPVOID lpParam)
{
for (DWORD i = 0; i <= 100000; ++i)
{
--total;
}
return 0;
}
int wmain(int argc, WCHAR* argv[]) {
DWORD dwThreadID[2];
HANDLE hThread[2];
hThread[0] =
CreateThread(
NULL, 0,
ThreadProcAdd, NULL,
0, &dwThreadID[0]
);
hThread[1] =
CreateThread(
NULL, 0,
ThreadProcSub, NULL,
0, &dwThreadID[1]
);
if (hThread[0] == NULL || hThread[1] == NULL)
{
wprintf(_T("Thread creation fault! \n"));
}
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
wprintf(L"total: %d\n", total);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
return 0;
}
만약 위와 같이 전역 변수에 1씩 10만번 더하는 쓰레드와 1씩 10번 빼는 쓰레드가 있을 때 일반적으로는 출력되는 total이 0이될 것이라고 생각하기 쉽지만, 실제로 출력해보면 0과는 매우 떨어져있으며 심지어는 매번 값이 달라진다는 것을 알 수 있다.



이것은 생각해보면 당연한 일인데,
기본적으로 +1이라는 작업은 한번에 일어나지 않고,

레지스터에 저장 -> 연산 -> 메모리에 저장
하는 3단계에 걸쳐서 일어나기 때문에 연산 순서가 섞일 수 있다는 말이 된다.
일반적으로 사용되었던 C Runtime Library(CRT)는 Thread-safe하지 않다.
특히 strtok같은 경우는 함수가 같은 상태를 공유하기 때문에 여러 쓰레드에서 사용하면 원하지 않는 결과가 나올 수 있다.
이러한 경우 같은 기능이지만 Thread-safe한 함수를 사용해야 한다.
이 때 TLS(Thread Local Storgae)라는 개념을 사용한다.
Thread마다 공간을 따로 할당해 (매우 간단하게 Heap데이터를 떼어준다고 생각하면 된다.)
Thread-Safe한 함수는 TLS를 이용해 쓰레드마다 서로 다른 상태를 가져 이전의 문제를 해결한다.
앞의 _beginthreadex는 CreateThread와 다르게 TLS를 초기화 시켜주기 때문에, 대부분 CreateThread 대신 _beginthreadex를 사용한다.
_beginthread, _beginthreadex
자세한 정보: _beginthread, _beginthreadex
learn.microsoft.com
쓰레드의 상태 컨트롤

이전의 프로세스와 상태 변화 자체는 같다.
Suspend & Resume
Suspend는 Thread를 Block상태(엄밀히는 Suspend)로 만들며, Resume은 Block상태의 Thread를 Ready로 만든다.
Thread 커널 오브젝트에는 내부적으로 Suspend Count를 호출한다. 세마포와 약간 비슷하다.
SuspendThread 함수(processthreadsapi.h) - Win32 apps
지정된 스레드를 일시 중단합니다.
learn.microsoft.com
ResumeThread 함수(processthreadsapi.h) - Win32 apps
스레드의 일시 중단 횟수를 감소합니다. 일시 중단 횟수가 0으로 감소하면 스레드 실행이 다시 시작됩니다.
learn.microsoft.com
쓰레드의 우선순위 컨트롤
https://learn.microsoft.com/ko-kr/windows/win32/procthread/scheduling-priorities
일정 우선 순위 - Win32 apps
스레드는 예약 우선 순위에 따라 실행되도록 예약됩니다.
learn.microsoft.com
쓰레드의 우선순위는 프로세스 우선순위와 조합해서 사용된다.
합쳐져서 최종 Priority를 정한다.
'C, C++ > 뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] 16장 컴구조(4) (0) | 2025.07.13 |
|---|---|
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] 13장 쓰레드 동기화 기법1 (3) | 2025.07.08 |
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] 11장 쓰레드의 이해 (0) | 2025.06.10 |
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] 10장 컴구조(3) (0) | 2025.06.08 |
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] 9장 스케줄링 알고리즘과 우선순위 (2) | 2025.06.08 |