본문 바로가기

개인 공부/네트워크

네트워크P-009 (스레드 생성 & 종료)

스레드 생성 API

CreateThread() 함수

  • 스레드 생성 후 스레드 핸들을 리턴
  • OS의 스레드 관련 데이터 구조체를 간접적으로 참조하는 매개체 역할을 한다
#include <windows.h>
HANDLE CreateThread(
	LPSECURITY_ATTRIBUTES lpThreadAttributes,	// 스레드 보안 특성 -> 보안을 위해 디폴트 NULL
	SIZE_T dwStackSize,	// 스레드에 할당되는 스택 크기
	LPTHREAD_START_ROUTINE lpStartAddress,	// 스레드 함수 호출
	LPVOID lpParameter,	// 스레드 함수에 전달할 매개변수(인자)
	DWORD dwCreationFlags, // 스레드 제어하는 플래그
	LPDWORD lpThreadId	// 스레드의 ID 값
);
  • lpThreadAttributes : 보안 디스크립터 정보를 전달한다!! -> 보안을 위해 NULL 포인터를 전달한다
  • dwStackSize : 스레드에 할당되는 스택 크기!!
  • lpStartAddress : 스레드 함수 호출
  • lpParameter : 스레드의 함수에 전달할 매개변수 지정
    • 포인터 크기보다 작거나 같은 데이터는 값 또는 주소형태로 전달한다 
    • 포인터 크기보다 큰 데이터는 값을 구조체나 배열에 넣고 주소 형태로 전달한다 
//	====== lpParameter전달 ======
DWORD WINAPI ThreadProc(LPVOID lpParameter){}

// ===== 크기에 따라 전달 방법 =====
int value = 값;
// 포인터와 크기가 같거나 작을 경우
CreateThread(..., (LPVOID)value)...	// 값의 형태로 전달
CreateThread(..., &(value))...	// 주소를 전달한다

// 포인터보다 큰 데이터 전달
struct Mydata data ={};
CreateThread(..., &data)	// 주소를 구조체로 전달
  • dwCreationFlags : 새로운 쓰레드 생성 이후에 바로 실행 가능한 상태, 대기 상태를 결정
    • 0 : 스레드 생성 후 바로 실행
    • CREATE_SUSPENDED : 스레드가 생성되지만 대기상태(ResumeThread()함수를 호출하기 전까지 대기)
  • lpThreadId : DWORD형 변수를 전달하면 스레드 ID가 저장된다

스레드 생성과 종료

  • 스레드의 최대 개수[32bit 윈도우] 

스레드 생성

  • 스레드 종료 방법
    1. 스레드 함수가 리턴한다
    2. 스레드 함수 내에서 ExitThread() 함수를 호출한다
    3. 다른 스레드가 TerminateThread() 함수를 호출한다
    4. 주 스레드가 종료하면 모든 스레드가 종료된다
  • 스레드 종료 함수
#include <windows.h>
void ExitThread(
	DWORD dwExitCode;	// 종료 코드
);

#include <winodws.h>
BOOL ThermianteThread(
	HANDLE hThread,	// 종료할 스레드를 가리키는 핸들
	DWORD dwExitCdoe	// 종료 코드
);
// 성공 : 0이 아닌 다른 값
// 실패 : 0
  • 스레드 함수 사용
DWORD WINAPI f(arg..){
	return 0; // 스레드 종료
}


int main (){
	// 스레드1 생성!
	HANDLE hThread1 = CreateThread(NULL, 0, f, NULL, 0, NULL);
 	if(hThread1 == NULL)	{오류 처리}
	
	// 스레드2 생성
	HANDLE hThread2 = CreateThread(NULL, 0, f, NULL, 0, NULL);
	if(hThread2 == NULL)	{오류 처리}    
}

스레드 생성과 종료, 인자 전달하는 연습!!

// 스레드에 사용될 매개변수 구조체
struct Point3D {
	int x, y, z;
};
  • 1초(1000밀리초)마다 멈췄다가 구조체 형변환하고 스레드 ID를 생성한다
  • DWORD WINAPI GetCurrentThreadId(void)를 통해 스레드의 식별 번호를 가져온다!!
  • 스레드의 정보를 출력하고 스레드 함수를 리턴한다
//스레드 함수 정의
DWORD WINAPI MyThread(LPVOID arg) {
	Sleep(1000);	// 일시 정지 -> 1000 밀리초
	Point3D* pt = (Point3D*)arg;	

	// 출력
	printf("Running MyThread() %d : %d, %d, %d\n", GetCurrentThreadId(), pt->x, pt->y, pt->z);
	return 0;
}

메인 함수에서 스레드를 생성하고 종료를 한다

CreateThread()함수 : 스레드를 생성한다

CreateThread(보안, 스택 크기, 함수 호출, 주소 매개변수, 즉시실행, 스레드 ID );

CloseHandle() : 스레드 함수를 종료한다

int main() {
	// 첫 번째 스레드 생성!
	Point3D  pt1 = { 10,20,30 };
	HANDLE hThread1 = CreateThread(NULL, 0, MyThread ,&pt1, 0, NULL);
	// 보안, 크기, 스레드 함수 호출, 매개변수(주소), 0(즉시 실행), ID
	if (hThread1 == NULL)	return 1;
	
	CloseHandle(hThread1);

	// 두 번째 스레드 생성
	Point3D pt2 = { 90, 40, 60 };
	HANDLE hThread2 = CreateThread(NULL, 0, MyThread, &pt2, 0, NULL);
	if (hThread2 == NULL)	return 1;
	CloseHandle(hThread2);
	
    // 세 번째 스레드 생성
	Point3D pt3 = { 50, 70, 80 };
	HANDLE hThread3 = CreateThread(NULL, 0, MyThread, &pt3, 0, NULL);
	CloseHandle(hThread3);

	printf("Running main() %d\n", GetCurrentThreadId());
	Sleep(2000);
	return 0;
}

전체 코드

#include "Common.h"

struct Point3D {
	int x, y, z;
};

//스레드 함수 (공통으로 사용되는 작업) 정의

DWORD WINAPI MyThread(LPVOID arg) {
	Sleep(1000);	// 일시 정지 -> 1000 밀리초
	Point3D* pt = (Point3D*)arg;	

	// 출력
	printf("Running MyThread() %d : %d, %d, %d\n", GetCurrentThreadId(), pt->x, pt->y, pt->z);
	return 0;
}

int main() {
	// 첫 번째 스레드 생성!
	Point3D  pt1 = { 10,20,30 };
	HANDLE hThread1 = CreateThread(NULL, 0, MyThread ,&pt1, 0, NULL);
	// 보안, 크기, 스레드 함수 호출, 매개변수(주소), 0(즉시 실행), ID
	if (hThread1 == NULL)	return 1;
	CloseHandle(hThread1);

	// 두 번째 스레드 생성
	Point3D pt2 = { 90, 40, 60 };
	HANDLE hThread2 = CreateThread(NULL, 0, MyThread, &pt2, 0, NULL);
	if (hThread2 == NULL)	return 1;
	CloseHandle(hThread2);
	// 세 번째 스레드 생성
	Point3D pt3 = { 50, 70, 80 };
	HANDLE hThread3 = CreateThread(NULL, 0, MyThread, &pt3, 0, NULL);
	CloseHandle(hThread3);

	printf("Running main() %d\n", GetCurrentThreadId());
	Sleep(2000);
	return 0;
}

첫 번째 실행
두 번째 실행

 

이 프로그램을 실행 시키면 스레드가 실행되는 순서가 랜덤이라는 것을 알 수 있다

우리가 원하는 순서로 스레드를 실행 시키려면 스레드를 제어해야한다!!


스레드 제어(간단히)

  • 스레드는 윈도우 운영체제의 실행 단위므로, 우선 순위를 변경하거나 실행을 중지하고 재시작하는 등의 제어기능을 윈도우 API에서 지원해준다
  • 스레드 우선 순위 변경
    • 각 스레드에 CPU 시간을 적절히 분배하기 위한 정책!!
    • 스레드 스케줄링 OR CPU스케줄링 --> 우선 순위가 높은 것을 우선적으로 할당한다!
    • 스레드 우선 순위를 결정하는 요소
      • 프로세스 우선 순위 (우선 순위 클래스) : 프로세스 속성으로, 같은 프로세스가 생성한 스레드는 우선순위 클래스가 모두 같다!!!
      • 스레드 우선 순위 (우선 순위 레벨) : 스레드 속성으로, 같은 프로세스에 속한 스레드 간 상대적인 우선 순위를 결정할 때 사용한다