KimSangLab 2017. 12. 29. 15:14

다중 스레드 환경에서 코드가 정상적으로 동작하기 위한 기본 지침은 다음과 같다.
 1) 각 스레드에서만 사용할 변수는 전역 변수가 아니어야 한다. 개별 스레드만 접근 할 수 있도록 자료구조 또는 TLS에 두어야 한다.
 2) 한 함수를 여러 스레드들이 호출하여 어떠한 스레드 고유의 상태 값이 여러 호출들에 걸쳐서 유지되어야 하는 경우 그 상태 값을 전역 변수나 전역 자료구조에 저장하지 말고 TLS나 해당 스레드의 고유한 자료구조에 저장해야 한다.
 3) 스레드를 일시 정지 상태로 생성하지 않는 경우 초기화되지 않은 변수들 같은 경쟁 조건을 피해야 한다.
 4) 스레드가 표준 입력, 출력 핸들들을 설정한다거나 환경 변수들을 변경하는 일은 하지 말아야 한다. 기본 스레드가 다른 어떤 스레드들을 생성하기 전에 환경을 변경한다면 모든 스레드는 동일한 관경을 고유하게 된다. 
 5) 모든 스레드가 공유하는 변수는 정적 또는 전역 저장소에 있어야 하며, 메모리 장벽을 생성하는 동기화 또는 상호잠금 메커니즘들로 변수를 보호해야한다.

1. CRITICAL_SECTION 
  CRITICAL_SECTION 객체는 초기화 되고 삭제되지만 핸들을 가지지는 않으며, 다른 프로세스들과 공유할 수도 없다. CRITICAL_SECTION 형식의 한 변수는 하나의 임계코드 영역을 대표한다. 스레드들은 이 변수를 통해 임계 코드 영역으로 들어가거나 영역에서 벗어난다. 한 시점에서 특정 CRITICAL_SECTION 에는 하나의 스레드만 존재할 수 있다. CRITICAL_SECTION 변수와 관련 자원들을 초기화 하고 삭제할때는 각각 InitializeCriticalSection 함수와 DeleteCriticalSection 함수를 사용한다. EnterCriticalSection 호출이 반환되면 스레드는 임계 영역에 진입한 것이며 이를 스레드가 CRITICAL_SECTION 을 소유했다라고 한다. LeaveCriticialSection 을 호출하면 CRITICAL_SECTION 에 대한 소유권이 해제되고 스레드가 임계영역에서 벗어난다. CRITICAL_SECTION 의 소유권은 반드시 반환해야 한다. 그렇지 않으면 다른 스레드가 영원히 기다리게 된다. 소유한 스레드가 종료되더라고 소유권은 자동으로 반환되지 않으며 __finally 블록을 이용하여 정리 하는 방법을 권장한다. CRITICAL_SECTION 은 커널 객체가 아니라는 장점과 사용자 공간에서 관리된다는 장점이 있다. 비슷한 기능을 가진 Windows 뮤텍스 커널 객체에 비해 거의 항상 성능상의 이점을 제공한다.

2. 뮤텍스(Mutex)
  Mutual Exclusion(상호 배제) 의 준말인 Mutex 는 CRITICAL_SECTION 이상의 잠금 기능성을 제공한다. 뮤텍스에는 이름을 붙일 수 있고 핸들도 있기 때문에 서로 다른 프로세스의 스레드들 사이의 프로세스 간 동기화에 이용할 수 있다. CRITICAL_SECTION 에 비해 뮤텍스는 만료시간을 지정할 수 있으며 프로세스가 종료되었을때 신호될 수 있다는 장점이 있다.

3. 뮤텍스 vs CRITICAL_SECTION 
 1) 두 객체 모두 한 스레드가 객체를 소유하고 있으면 다른 스레드들은 객체가 해제될 때까지 차단된다.
 2)  스레드 종료에 의해 폐기된 뮤텍스는 신호된 상태가 되므로 다른 스레드들이 영원히 차단되는 일이 없다. 하지만 폐기된 뮤텍스가 있다면 프로그램에 심각한 버그나 실패 상황이 존재함이 거의 확실하다.
 3) 뮤텍스는 대기 만료 시간을 지정할 수 있다. CRITICAL_SECTION 은 주기적으로 점검해야한다.
 4) 뮤텍스는 이름을 가질 수 있으며 서로 다른 프로세스의 스레드들이 공유할 수 있다.
 5) 뮤텍스를 생성할때 스레드가 그 뮤텍스를 즉시 소유하게 할 수 있다.
 6) CRITICAL_SECTION 은 거의 항상 뮤텍스 보다 훨씬 빠르다.

4. 세마포어
  하나의 세마포어는 하나의 횟수가 부여된다. 그 횟수가 0보다 크면 세마포 객체는 신호된 상태이고 횟수가 0이면 신호되지 않은 상태이다. 스레드는 두 가지 대기 함수들 중 하나를 사용해서 보통의 방식대로 세마포어를 기다린다. 대기 스레드가 해제되면 세마포어의 횟수가 1 감소한다. 세마포어 함수는 CreateSemaphore, CreateSemaphoreEx, OpenSemaphore, ReleaseSemaphore 가 있다. 세마포어어 횟수를 한번에 2 감소시키는 방법은 존재하지 않기 때문에 CRITICAL_SECTION 을 이용하여 구현해야 한다.

5. 이벤트
  이벤트는 어떠한 조건( 사용 가능한 메시지가 있음 ) 이 이제 성립함을 다른 스레드들에게 신호하는데 쓰인다. 한 이벤트가 신호되었을 때 그것을 기다리는 여러 개 스레드들이 동시에 해제된다. 이벤트는 수동 재설정 이벤트와 자동 재 설정 이벤트로 나뉜다.
  1) 수동 재설정 이벤트는 이벤트를 기다리는 여러 스레드들에게 신호할 수 있다.
  2) 자동 재설정 이벤트는 이벤트를 기다리는 하나의 스레드에게 신호한다. 이벤트는 자동으로 재설정 된다.
  이벤트를 다루는 함수는 CreateEvent, CreateEventEx, OpenEvent, SetEvent, ResetEvent, PulseEvent 이다. SetEvent 는 스레드가 이벤트에 신호를 할 때 쓰인다. 지정된 이벤트가 자동 재설정이면 하나의 대기 스레드가 해제 되며, 이벤트는 자동적으로 신호되지 않은 상태로 돌아간다.