2016년 5월 25일 수요일

뇌자극 TCP_IP 프로그래밍 15강 요약

1. 윈도우 멀티 스레드 기술 소개

멀티 스레드 기술은 os 종속 기술이 아니기에 기본적으로는 리눅스 멀티 스레드와 개념은 같다. 단지 사용범위와 스레드 API가 다를 뿐이다.

리눅스는 주로 멀티 프로세스를 선호하고
윈도우는 주로 멀티 스레드를 선호한다.

2. 윈도우 스레드 프로그래밍

윈도우와 리눅스와 별 다르지 않다. 각 과정에서 사용되는 함수의 이름과 매개 변수가 차이가 있을 뿐이다.

윈도우 스레드에 대한 이해

커널 객체 - 커널에서 생성된 여러 자원(세마포어, 뮤텍스, 이벤트, 파이프)등을 의미한다
커널 객체는 직접 접근하지 못하고 대신에 핸들을 이용해서 제어한다.

커널 객체는 신호 상태와 비신호 상태를 가질 수 있으며 이 신호 상태를 검사해서 상태를 확인 할 수 있다. 일종의 시그널 시스템이다.

스레드 생성

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in         SIZE_T dwStackSize,
  __in         LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in         DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

lpThreadAttributes : NULL이면 기본 보안 특성을 사용한다.
dwStackSize : 스레드는 자신만의 스텍 공간을 가지는데 스텍 공간을 지정할 수 있다.
lpStartAddress : 워커 스레드가 실행할 코드를 가지고 있는 스레드 함수다
lpParameter : 스레드 함수에 넘길 매개변수다.
dwCreationFlags : 스레드가 만들어질 때 즉시 실행할지 아니면 대기할지 정의 한다.
 0이면 스레드는 즉시 실행한다. CREATE_SUSPENDED 플래그를 설정하면 ResumThread 함수를 호출 할 때까지 대기한다. 주로 디버깅 용도로 사용한다.
lpThreadId : 스레드를 식별하기 위한 식별 번호다. 사용하지 않으려면 NULL을 지정한다.

스레드 종료

BOOL WINAPI CloseHandle(
  __in HANDLE hObject
);

스레드 종료 대기

이 함수들은 핸들을 매개변수로 받기 때문에 스레드 객체 뿐만 아니라, 핸들로 제어되는 모든 종류의 객체를 기다리기 위해서 사용할 수 있다.

DWORD WINAPI WaitForMultupleObjects(
  __in DWORD nCount,
  __in const HANDLE *lpHandles,
  __in BOOL bWaitAll,
  __in DWORD dwMilliseconds
);

nCount : 여러 객체의 시그널 상태를 기다릴 때 몇 개의 객체를 기다릴지 정할 수 있다.
lpHandles : 기다릴 핸들의 배열
bWaitAll : TRUE 면 배열에 있는 모든 핸들이 시그널 상태가 되어야 반환한다.
dwMilliseconds : 밀리 초 단위로 기다리는 시간에 제한을 둘 수 있다. 0을 지정하면 바로 반환된다. INFINITE로 하면 모든 핸들이 시그널 상태가 되어야 반환한다.

단지 한 객체의 시그널 상태를 기다리는 것을 제외하면  WaitForMultupleObjects 와 같다.

DWORD WINAPI WaitForSingleObjects(
  __in HANDLE hHandle,
  __in DWORD dwMilliseconds
);

hHandle : 시그널 상태를 가디리가 위한 핸들
dwMilliseconds : 기다리기 위한 제한 시간

이들 함수로 스레드 핸들이 시그널 상태에 놓이길 기다린다.
시그널 상태라는 것은 스레드가 종료되어 핸들의 상태에 변화가 생겼음을 의미한다.
모든 핸들러가 시그널 상태에 놓여서 함수가 반환되면 그때 스레드 핸들을 제거하면 된다.

3. 뮤텍스를 이용한 윈도우 스레드의 동기화

스레드간 자원 공유를 위해 접근 제어를 꼭 해줘야 하며, 이때 리눅스와 마찬가지로 임계영역을 만들어 스레드의 진입을 제어한다.

유저 모드 동기화

윈도우는 임계 영역 객체를 이영해서 관리한다.
임계 영역 객체를 초기화하고, 임계 영역을 진입하고, 떠나고, 
더 사용하지 않는 임계 영역 객체는 해제하면 된다.

임계영역 초기화

void WINAPI InitializeCriticalSection(
  __out LPCRITICAL_SECTION lpCriticalSection
);

임계 영역 진입과 떠나기

void WINAPI EnterCriticalSection(
  __inout LPCRITICAL_SECTION lpCriticalSection
);

void WINAPI LeaveCriticalSection(
  __inout LPCRITICAL_SECTION lpCriticalSection
);

임계 영역 객체는 유저 모드에서 작동하는 동기화 도구다.
커널 모드 객체를 다루기 위해서는 유저모드 -> 커널모드-> 유저모드 로의 
모드 전환이 일어나는데 유저 모드에서 작동하는 임계 영역 객체는 
모드 전환이 이루어지지 않으므로 커널 코드에서 작동하는 다른 객체보다 
더 빠르게 작동한다.

커널 모드 동기화

HANDLE WINAPI CreatMutex(
  __in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes,
  __in BOOL bInitalOwner,
  __in_opt LPCTSTR lpName
);

lpMutexAttributes : NULL이면 기본 특성이 사용되며 자식 프로세스에 상속할 수 없다.
bInitalOwner : 뮤텍스를 생성한 스레드에 뮤텍스 소유권을 제공할지 여부를 결정한다. 
TRUE를 지정하면 뮤텍스의 소유권은 작성 스레드에게 넘어가고, 소유권은 비 신호 상태가 된다.
FALSE를 지정하면 소유권을 요구하지 않게 된다.
lpName : 뮤텍스는 전역 커널 객체로서 다른 프로세스와 함께 사용할 수 있다. 다른 프로세스와 함께 사용하기 위해서는 뮤텍스의 고유 이름을 알고 있어야 한다. 뮤텍스에 이름을 지어 준다.

임계 영역 신호 상태

뮤텍스를 얻은 스레드가 작업을 마치면 함수를 호출하여 임계영역을 빠져나가게 된다.
이 함수를 호출하면 뮤텍스 객체는 신호 상태가 되고 다른 스레드가 뮤텍스를 얻을 수 있게 된다.

BOOL WINAPI ReleaseMutex(
  __in HANDLE hMutex
);

4. 조건 변수를 이용한 스레드 동기화

조건 변수 역시 스레드 동기화를 위해서 사용한다.
조건 변수를 이용하면 일정한 조건을 만족할 때까지 스레드를 기다리게 할 수 있다.

조건 변수의 생성과 초기화

VOID WINAPI InitalizeConditionVariable (
  __out PCONDITION_VARIABLE ConditionVariable
);

ConditionVariable : 초기화할 조건 변수

조건 변수로부터 신호 대기

BOOL WINAPI SleepConditionVariableCS (
  __inout PCONDITION_VARIABLE ConditionVariable,
  __inout PCRITICAL_SECTION CriticalSection,
  __in DWORD dwMilliseconds
);

ConditionVariable : 신호를 기다릴 조건 변수
CriticalSection : 조건 변수의 진입을 제어하기 위해서 뮤텍스 객체를 사용한다.
dwMilliseconds : 신호를 기다릴 제한 시간을 밀리 초 단위로 설정한다. 0이면 바로 반환,
무한정 기다리게 하려면 INFINITE를 설정

조건 변수에 신호 전송

신호 대기 -> 신호 전달 -> 깨움 의 매커니즘을 가진다.

//하나의 대기 중인 스레드를 깨울 때
VOID WINAPI WakeConditionVariable(
  __inout PCONDITION_VARIABLE ConditionVariable
);

//모든 대기 중인 스레드를 깨울 때
VOID WINAPI WakeAllConditionVariable(
  __inout PCONDITION_VARIABLE ConditionVariable
);

5. 멀티 스레드 기반의 소켓 프로그램 제작

멀티 스레드 소켓 프로그램의 흐름

1) socket() -> bind() -> listen() -> accept() 로 이루어지는 흐름은 pthread와 같다.
2) accept 함수가 성공적으로 호출되어서 연결 소켓이 만들어지면, CreatThread 함수로 새로운 스레드를 만든다. 스레드 함수의 매개변수로 연결 소켓이 전달된다.
3) 메인 스레드는 즉시 accept 함수를 호출해야 하기 때문에 스레드의 종료를 기다리지 않고 (CloseHandle 함수로) 핸들을 닫는다.

위의 내용은 소스 코드를 보고 익히자
역시 깃허브에 소스 코드를 올렸다.

댓글 없음:

댓글 쓰기