340 likes | 515 Views
Chapter 05. 멀티스레드. 학습 목표. 멀티스레드의 필요성을 이해하고 기본 개념을 익힌다 . 멀티스레드를 이용하여 TCP 서버를 작성한다 . 스레드 동기화 기법을 익힌다. 프로세스와 스레드 (1/2). 용어 프로세스 (process) 메모리를 비롯한 각종 리소스를 담고 있는 컨테이너 (container) 로서 정적인 개념 스레드 (thread) 실제 CPU 시간을 할당받아 수행되는 실행 단위로서 동적인 개념 주 스레드 (primary thread)
E N D
Chapter 05. 멀티스레드
학습 목표 • 멀티스레드의 필요성을 이해하고 기본 개념을 익힌다. • 멀티스레드를 이용하여 TCP 서버를 작성한다. • 스레드 동기화 기법을 익힌다.
프로세스와 스레드 (1/2) • 용어 • 프로세스(process) • 메모리를 비롯한 각종 리소스를 담고 있는 컨테이너(container)로서 정적인 개념 • 스레드(thread) • 실제 CPU 시간을 할당받아 수행되는 실행 단위로서 동적인 개념 • 주 스레드(primary thread) • main() 또는 WinMain() 함수에서 시작되는 스레드로, 프로세스가 시작할 때 생성 • 컨텍스트 전환(context switch) • CPU와 운영체제의 협동으로 이루어지는 스레드 실행 상태의 저장과 복원 작업
① ② ① ② 스레드① 레지스터 CPU CPU 스레드② 레지스터 ① ② ① ② 스레드① 레지스터 CPU CPU 스레드② 레지스터 프로세스와 스레드 (2/2) • 컨텍스트 전환 과정
스레드 생성과 종료 (1/6) • 스레드 생성에 필요한 요소 • 스레드 함수(thread function)의 시작 주소 • 스레드 함수 실행시 사용할 스택 영역의 크기
스레드 ①, ② f() { ... } main() { ... } 코드 주 스레드 주 스레드의 실행 스택 스레드 ①의 실행 스택 스레드 ②의 실행 스택 스레드 생성과 종료 (2/6) • 프로세스의 주소 공간 • 두 개의 함수 • 세 개의 스레드
스레드 생성과 종료 (3/6) • CreateThread() 함수 • 스레드를 생성한 후 스레드 핸들(thread handle)을 리턴 HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, // NULL SIZE_T dwStackSize, // 0 LPTHREAD_START_ROUTINE lpStartAddress, // 스레드 함수 LPVOID lpParameter, // 스레드 함수 인자 DWORD dwCreationFlags, // 0 또는 CREATE_SUSPENDED LPDWORD lpThreadId // 스레드 ID ) ; 성공: 스레드 핸들, 실패: NULL
스레드 생성과 종료 (4/6) • 스레드 함수 정의 DWORD WINAPI ThreadProc (LPVOID lpParameter) { ... }
스레드 생성과 종료 (5/6) • 스레드 종료 방법 ① 스레드 함수가 리턴 ② 스레드 함수 내에서 ExitThread() 함수를 호출 ③ TerminateThread() 함수를 호출 ④ 주 스레드가 종료하면 모든 스레드가 종료
스레드 생성과 종료 (6/6) • 스레드 종료 함수 void ExitThread ( DWORD dwExitCode // 종료 코드 ) ; BOOL TerminateThread ( HANDLE hThread, // 종료할 스레드를 가리키는 핸들 DWORD dwExitCode // 종료 코드 ) ; 성공: 0이 아닌 값, 실패: 0
스레드 조작 –우선 순위 (1/5) • 용어 • 스레드 스케줄링(thread scheduling) • 윈도우가 각 스레드에게 CPU 시간을 적절히 분배하기 위한 정책 • 우선순위 클래스(priority class) • 프로세스 속성으로, 한 프로세스가 생성한 스레드는 모두 동일한 우선순위 클래스를 가짐 • 우선순위 레벨(priority level) • 스레드 속성으로, 한 프로세스에 속한 스레드 사이에서 상대적인 우선순위를 결정할 때 사용 • 기초 우선순위(base priority) • 우선순위 클래스와 우선순위 레벨을 결합한 값으로, 스레드 스케줄링에 사용
스레드 조작 –우선 순위 (2/5) • 우선 순위 클래스 • REALTIME_PRIORITY_CLASS(실시간) • HIGH_PRIORITY_CLASS(높음) • ABOVE_NORMAL_PRIORITY_CLASS(보통 초과; 윈도우2000/XP/2003) • NORMAL_PRIORITY_CLASS(보통) • BELOW_NORMAL_PRIORITY_CLASS(보통 미만; 윈도우2000/XP/2003) • IDLE_PRIORITY_CLASS(낮음)
스레드 조작 –우선 순위 (3/5) • 우선 순위 레벨 • THREAD_PRIORITY_TIME_CRITICAL • THREAD_PRIORITY_HIGHEST • THREAD_PRIORITY_ABOVE_NORMAL • THREAD_PRIORITY_NORMAL • THREAD_PRIORITY_BELOW_NORMAL • THREAD_PRIORITY_LOWEST • THREAD_PRIORITY_IDLE
(낮음) 기초 우선순위 (높음) ... 세 개의 스레드를 교대로 수행 스레드 스케줄러 CPU 스레드 조작 –우선 순위 (4/5) • 우선 순위 기반 스레드 스케줄링
스레드 조작 –우선 순위 (5/5) • 우선 순위 레벨 조작 함수 BOOL SetThreadPriority ( HANDLE hThread, // 스레드 핸들 int nPriority // 우선순위 레벨값 ) ; 성공: 0이 아닌 값, 실패: 0 int GetThreadPriority ( HANDLE hThread // 스레드 핸들 ) ; 성공: 우선순위 레벨값, 실패: THREAD_PRIORITY_ERROR_RETURN
스레드 조작 –스레드 종료 대기 (1/4) • WaitForSingleObject() 함수 • 특정 스레드가 종료할 때까지 대기 • WaitForSingleObject() 함수 사용 예 DWORD WaitForSingleObject ( HANDLE hHandle, DWORD dwMilliseconds ) ; 성공: WAIT_OBJECT_0 또는 WAIT_TIMEOUT, 실패: WAIT_FAILED HANDLE hThread = CreateThread(...); WaitForSingleObject(hThread, INFINITE);
스레드 조작 –스레드 종료 대기 (2/4) • WaitForMultipleObjects() 함수 • 두 개 이상의 스레드가 종료할 때까지 대기 DWORD WaitForMultipleObjects ( DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds ) ; 성공: WAIT_OBJECT_0 ~ WAIT_OBJECT_0 + nCount-1 또는 WAIT_TIMEOUT, 실패: WAIT_FAILED
스레드 조작 –스레드 종료 대기 (3/4) • WaitForMultipleObjects() 함수 사용 예 ① // 모든 스레드 종료를 기다릴 경우 HANDLE hThread[2]; HANDLE hThread[0] = CreateThread(...); HANDLE hThread[1] = CreateThread(...); WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
스레드 조작 –스레드 종료 대기 (4/4) • WaitForMultipleObjects() 함수 사용 예 ② // 두 스레드 중 하나의 종료를 기다릴 경우 HANDLE hThread[2]; HANDLE hThread[0] = CreateThread(...); HANDLE hThread[1] = CreateThread(...); DWORD retval = WaitForMultipleObjects(2, hThread, FALSE, INFINITE); switch(retval){ case WAIT_OBJECT_0: // hThread[0] 종료 break; case WAIT_OBJECT_0+1: // hThread[1] 종료 break; case WAIT_FAILED: // 오류 발생 break; }
스레드 조작 –실행 중지와 재실행 (1/2) • 실행 중지 함수 ① • 재실행 함수 DWORD SuspendThread ( HANDLE hThread // 스레드 핸들 ) ; 성공: 중지 횟수, 실패: -1 DWORD ResumeThread ( HANDLE hThread // 스레드 핸들 ) ; 성공: 중지 횟수, 실패: -1
스레드 조작 –실행 중지와 재실행 (2/2) • 실행 중지 함수 ② • 스레드가 실행을 멈추고 일정 시간동안 대기 void Sleep ( DWORD dwMilliseconds // 밀리초(ms) ) ;
멀티스레드 TCP 서버 (1/3) • 기본 구조 DWORD WINAPI ProcessClient(LPVOID arg) { // 전달된 소켓 ③ SOCKET client_sock = (SOCKET)arg; // 클라이언트 정보 얻기 ④ addrlen = sizeof(clientaddr); getpeername(client_sock, (SOCKADDR *)&clientaddr, &addrlen); // 클라이언트와 데이터 통신 ⑤ while(1){ ... } closesocket(client_sock); return 0; }
멀티스레드 TCP 서버 (2/3) • 기본 구조 (cont’d) int main(int argc, char* argv[]) { ... while(1){ // 클라이언트 접속 수용 ① client_sock = accept(listen_sock, ...); ... // 스레드 생성 ② CreateThread(NULL, 0, ProcessClient, (LPVOID)client_sock, 0, &ThreadId); } ... }
멀티스레드 TCP 서버 (3/3) • 소켓과 연관된 주소 정보 얻기 int getpeername ( SOCKET s, struct sockaddr* name, int* namelen ) ; 성공: 0, 실패: SOCKET_ERROR int getsockname ( SOCKET s, struct sockaddr* name, int* namelen ) ; 성공: 0, 실패: SOCKET_ERROR
공유 변수 int money = 1000 스레드 1 스레드 2 ... ① read money into ECX ② ECX = ECX + 2000 ③ write ECX into money ... ... ① read money into ECX ② ECX = ECX + 4000 ③ write ECX into money ... 스레드 동기화 (1/4) • 스레드 동기화(thread synchronization) 필요성 • 멀티스레드를 사용하는 프로그램에서 두 개 이상의 스레드가 공유 데이터를 접근하는 경우
종류 주요 용도 임계영역 (critical section) 공유 리소스에 대해 오직 하나의 스레드 접근만 허용 (한 프로세스에 속한 스레드에만 사용 가능) 뮤텍스 (mutex) 공유 리소스에 대해 오직 하나의 스레드 접근만 허용 (서로 다른 프로세스에 속한 스레드에도 사용 가능) 이벤트 (event) 특정 사건 발생을 다른 스레드에게 알림 세마포 (semaphore) 한정된 개수의 자원을 여러 스레드가 사용하려고 할 때, 접근을 제한 대기 가능 타이머 (waitable timer) 특정 시간이 되면 대기 중인 스레드를 깨움 스레드 동기화 (2/4) • 다양한 스레드 동기화 기법
매개체 스레드 1 스레드 2 진행 대기 스레드 동기화 (3/4) • 스레드 동기화 원리
비신호 상태 신호 상태 동기화 객체 동기화 객체 Wait*() 함수로 감지 스레드 동기화 (4/4) • 스레드 동기화 원리 (cont’d)
임계 영역 (1/3) • 임계 영역(critical section) • 두 개 이상의 스레드가 공유 리소스를 접근할 때, 오직 하나의 스레드 접근만 허용해야 하는 경우에 사용 • 특징 • 유저(user) 영역 메모리에 존재하는 구조체이므로 한 프로세스에 속한 스레드 동기화에만 사용 가능 • 일반적인 동기화 객체보다 빠르고 효율적
임계 영역 (2/3) • 임계 영역 사용 예 #include <windows.h> CRITICAL_SECTION cs; // ① // 스레드 1 DWORD WINAPI Thread1(LPVOID arg) { ... EnterCriticalSection(&cs); // ③ // 공유 리소스 접근 LeaveCriticalSection(&cs); // ④ ... }
임계 영역 (3/3) • 임계 영역 사용 예 (cont’d) // 스레드 2 DWORD WINAPI Thread2(LPVOID arg) { ... EnterCriticalSection(&cs); // ③ // 공유 리소스 접근 LeaveCriticalSection(&cs); // ④ ... } int main() { ... InitializeCriticalSection(&cs); // ② // 스레드 생성과 종료 DeleteCriticalSection(&cs); // ⑤ ... }
이벤트 객체 (1/3) • 이벤트 객체(event object) • 특정 사건 발생을 다른 스레드에게 알릴 때 주로 사용 • 이벤트 객체를 이용한 동기화 예 ① 이벤트 객체를 비신호 상태로 생성 ② 한 스레드가 작업을 진행하고, 나머지 스레드는 이벤트 객체에 대해 Wait*() 함수를 호출함으로써 이벤트 객체가 신호 상태가 되기를 기다림 ③ 스레드가 작업을 완료하면, 이벤트를 신호 상태로 바꿈 ④ 기다리고 있던 모든 스레드가 깨어나서 작업을 진행
이벤트 객체 (2/3) • 이벤트 객체 상태 변경 • 이벤트 객체의 종류 • 자동 리셋(auto-reset) 이벤트 • 수동 리셋(manual-reset) 이벤트 BOOL SetEvent (HANDLE hEvent) ; // 비신호 상태 신호 상태 BOOL ResetEvent (HANDLE hEvent) ; // 신호 상태 비신호 상태
이벤트 객체 (3/3) • 이벤트 객체 생성 HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) ; 성공: 이벤트 핸들, 실패: NULL