chapter 05. 멀티스레드
DESCRIPTION
Chapter 05. 멀티스레드. 학습 목표. 멀티스레드의 필요성을 이해하고 기본 개념을 익힌다 . 멀티스레드를 이용하여 TCP 서버를 작성한다 . 스레드 동기화 기법을 익힌다. 프로세스와 스레드 (1/2). 용어 프로세스 (process) 메모리를 비롯한 각종 리소스를 담고 있는 컨테이너 (container) 로서 정적인 개념 스레드 (thread) 실제 CPU 시간을 할당받아 수행되는 실행 단위로서 동적인 개념 주 스레드 (primary thread) - PowerPoint PPT PresentationTRANSCRIPT
Chapter 05.
멀티스레드
- 2 -
학습 목표
• 멀티스레드의 필요성을 이해하고 기본 개념을 익힌다 .
• 멀티스레드를 이용하여 TCP 서버를 작성한다 .• 스레드 동기화 기법을 익힌다 .
- 3 -
프로세스와 스레드 (1/2)
• 용어– 프로세스 (process)
• 메모리를 비롯한 각종 리소스를 담고 있는 컨테이너 (container)로서 정적인 개념
– 스레드 (thread)• 실제 CPU 시간을 할당받아 수행되는 실행 단위로서 동적인 개념
– 주 스레드 (primary thread)• main() 또는 WinMain() 함수에서 시작되는 스레드로 ,
프로세스가 시작할 때 생성
– 컨텍스트 전환 (context switch)• CPU 와 운영체제의 협동으로 이루어지는 스레드 실행 상태의
저장과 복원 작업
- 4 -
프로세스와 스레드 (2/2)
• 컨텍스트 전환 과정
CPU
① ②
CPU
① ② 스레드①레지스터
스레드②레지스터
CPU
① ②
CPU
① ② 스레드①레지스터
스레드②레지스터
- 5 -
스레드 생성과 종료 (1/6)
• 스레드 생성에 필요한 요소– 스레드 함수 (thread function) 의 시작 주소– 스레드 함수 실행시 사용할 스택 영역의 크기
- 6 -
스레드 생성과 종료 (2/6)
• 프로세스의 주소 공간– 두 개의 함수– 세 개의 스레드
f(){ ...}main(){ ...}
주 스레드의실행 스택
스레드 ①의실행 스택
코드
스레드 ②의실행 스택
주 스레드
스레드 ① , ②
- 7 -
스레드 생성과 종료 (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
- 8 -
스레드 생성과 종료 (4/6)
• 스레드 함수 정의
DWORD WINAPI ThreadProc (LPVOID lpParameter){ ...}
- 9 -
스레드 생성과 종료 (5/6)
• 스레드 종료 방법① 스레드 함수가 리턴② 스레드 함수 내에서 ExitThread() 함수를 호출③ TerminateThread() 함수를 호출④ 주 스레드가 종료하면 모든 스레드가 종료
- 10 -
스레드 생성과 종료 (6/6)
• 스레드 종료 함수
void ExitThread ( DWORD dwExitCode // 종료 코드) ;
BOOL TerminateThread ( HANDLE hThread, // 종료할 스레드를 가리키는 핸들 DWORD dwExitCode // 종료 코드) ; 성공 : 0 이 아닌 값 , 실패 : 0
- 11 -
스레드 조작 – 우선 순위 (1/5)
• 용어– 스레드 스케줄링 (thread scheduling)
• 윈도우가 각 스레드에게 CPU 시간을 적절히 분배하기 위한 정책
– 우선순위 클래스 (priority class)• 프로세스 속성으로 , 한 프로세스가 생성한 스레드는 모두 동일한
우선순위 클래스를 가짐
– 우선순위 레벨 (priority level)• 스레드 속성으로 , 한 프로세스에 속한 스레드 사이에서 상대적인
우선순위를 결정할 때 사용
– 기초 우선순위 (base priority)• 우선순위 클래스와 우선순위 레벨을 결합한 값으로 , 스레드
스케줄링에 사용
- 12 -
스레드 조작 – 우선 순위 (2/5)
• 우선 순위 클래스– REALTIME_PRIORITY_CLASS( 실시간 )– HIGH_PRIORITY_CLASS( 높음 )– ABOVE_NORMAL_PRIORITY_CLASS( 보통 초과 ; 윈도우 2
000/XP/2003)– NORMAL_PRIORITY_CLASS( 보통 )– BELOW_NORMAL_PRIORITY_CLASS( 보통 미만 ; 윈도우 2
000/XP/2003)– IDLE_PRIORITY_CLASS( 낮음 )
- 13 -
스레드 조작 – 우선 순위 (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
- 14 -
스레드 조작 – 우선 순위 (4/5)
• 우선 순위 기반 스레드 스케줄링
...
스케줄러
CPU
스레드
( 낮음 ) 기초 우선순위 ( 높음 )
세 개의 스레드를교대로 수행
- 15 -
스레드 조작 – 우선 순위 (5/5)
• 우선 순위 레벨 조작 함수
BOOL SetThreadPriority ( HANDLE hThread, // 스레드 핸들 int nPriority // 우선순위 레벨값) ; 성공 : 0 이 아닌 값 , 실패 : 0
int GetThreadPriority ( HANDLE hThread // 스레드 핸들) ; 성공 : 우선순위 레벨값 , 실패 : THREAD_PRIORITY_ERROR_RETURN
- 16 -
스레드 조작 – 스레드 종료 대기 (1/4)
• WaitForSingleObject() 함수– 특정 스레드가 종료할 때까지 대기
• WaitForSingleObject() 함수 사용 예
DWORD WaitForSingleObject ( HANDLE hHandle, DWORD dwMilliseconds) ; 성공 : WAIT_OBJECT_0 또는 WAIT_TIMEOUT, 실패 : WAIT_FAILED
HANDLE hThread = CreateThread(...);WaitForSingleObject(hThread, INFINITE);
- 17 -
스레드 조작 – 스레드 종료 대기 (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
- 18 -
스레드 조작 – 스레드 종료 대기 (3/4)
• WaitForMultipleObjects() 함수 사용 예 ①
// 모든 스레드 종료를 기다릴 경우HANDLE hThread[2];HANDLE hThread[0] = CreateThread(...);HANDLE hThread[1] = CreateThread(...);WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
- 19 -
스레드 조작 – 스레드 종료 대기 (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;}
- 20 -
스레드 조작 – 실행 중지와 재실행 (1/2)
• 실행 중지 함수 ①
• 재실행 함수
DWORD SuspendThread ( HANDLE hThread // 스레드 핸들) ; 성공 : 중지 횟수 , 실패 : -1
DWORD ResumeThread ( HANDLE hThread // 스레드 핸들) ; 성공 : 중지 횟수 , 실패 : -1
- 21 -
스레드 조작 – 실행 중지와 재실행 (2/2)
• 실행 중지 함수 ②– 스레드가 실행을 멈추고 일정 시간동안 대기
void Sleep ( DWORD dwMilliseconds // 밀리초 (ms)) ;
- 22 -
멀티스레드 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;}
- 23 -
멀티스레드 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); } ...}
- 24 -
멀티스레드 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
- 25 -
스레드 동기화 (1/4)
• 스레드 동기화 (thread synchronization) 필요성– 멀티스레드를 사용하는 프로그램에서 두 개 이상의
스레드가 공유 데이터를 접근하는 경우
스레드 1
int money = 1000
...
① read money into ECX
② ECX = ECX + 2000
③ write ECX into money
...
스레드 2...
① read money into ECX
② ECX = ECX + 4000
③ write ECX into money
...
공유 변수
- 26 -
스레드 동기화 (2/4)
• 다양한 스레드 동기화 기법종류 주요 용도임계영역(critical section)
공유 리소스에 대해 오직 하나의 스레드 접근만 허용( 한 프로세스에 속한 스레드에만 사용 가능 )
뮤텍스(mutex)
공유 리소스에 대해 오직 하나의 스레드 접근만 허용( 서로 다른 프로세스에 속한 스레드에도 사용 가능 )
이벤트(event)
특정 사건 발생을 다른 스레드에게 알림
세마포(semaphore)
한정된 개수의 자원을 여러 스레드가 사용하려고 할 때 , 접근을 제한
대기 가능 타이머(waitable timer)
특정 시간이 되면 대기 중인 스레드를 깨움
- 27 -
스레드 동기화 (3/4)
• 스레드 동기화 원리
스레드 1 매개체 스레드 2
진행 대기
- 28 -
스레드 동기화 (4/4)
• 스레드 동기화 원리 (cont’d)
동기화 객체 동기화 객체
비신호 상태 신호 상태
Wait*() 함수로 감지
- 29 -
임계 영역 (1/3)
• 임계 영역 (critical section)– 두 개 이상의 스레드가 공유 리소스를 접근할 때 , 오직
하나의 스레드 접근만 허용해야 하는 경우에 사용
• 특징– 유저 (user) 영역 메모리에 존재하는 구조체이므로 한
프로세스에 속한 스레드 동기화에만 사용 가능– 일반적인 동기화 객체보다 빠르고 효율적
- 30 -
임계 영역 (2/3)
• 임계 영역 사용 예
#include <windows.h>
CRITICAL_SECTION cs; // ①
// 스레드 1DWORD WINAPI Thread1(LPVOID arg){ ... EnterCriticalSection(&cs); // ③ // 공유 리소스 접근 LeaveCriticalSection(&cs); // ④ ...}
- 31 -
임계 영역 (3/3)
• 임계 영역 사용 예 (cont’d)// 스레드 2DWORD WINAPI Thread2(LPVOID arg){ ... EnterCriticalSection(&cs); // ③ // 공유 리소스 접근 LeaveCriticalSection(&cs); // ④ ...}
int main(){ ... InitializeCriticalSection(&cs); // ② // 스레드 생성과 종료 DeleteCriticalSection(&cs); // ⑤ ...}
- 32 -
이벤트 객체 (1/3)
• 이벤트 객체 (event object)– 특정 사건 발생을 다른 스레드에게 알릴 때 주로 사용
• 이벤트 객체를 이용한 동기화 예① 이벤트 객체를 비신호 상태로 생성② 한 스레드가 작업을 진행하고 , 나머지 스레드는 이벤트
객체에 대해 Wait*() 함수를 호출함으로써 이벤트 객체가 신호 상태가 되기를 기다림
③ 스레드가 작업을 완료하면 , 이벤트를 신호 상태로 바꿈④ 기다리고 있던 모든 스레드가 깨어나서 작업을 진행
- 33 -
이벤트 객체 (2/3)
• 이벤트 객체 상태 변경
• 이벤트 객체의 종류– 자동 리셋 (auto-reset) 이벤트– 수동 리셋 (manual-reset) 이벤트
BOOL SetEvent (HANDLE hEvent) ; // 비신호 상태 신호 상태BOOL ResetEvent (HANDLE hEvent) ; // 신호 상태 비신호 상태
- 34 -
이벤트 객체 (3/3)
• 이벤트 객체 생성
HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName) ; 성공 : 이벤트 핸들 , 실패 : NULL