동기화객체 비교

-- VC++ 2011. 8. 26. 15:38
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

동기화 객체

 

두 개 이상의 thread가 공유자원에 대해 동시에 접근할 때, 데이터의 일관성을 보장할 수 없기 때문에 이를 막기 위해 공유자원을 적절히 사용할 수 있게 하는 방법을 동기화라 한다.

 

동기화 객체에는 아래와 같이 user-mode 객체와 kernel-mode 객체로 나뉜다.

 

object

Linux/Unix

Windows

File Lock

kernel-mode

kernel-mode

Critical Section

없음

user-mode

한 프로세스 내에서만 사용가능

Interlock

없음

user-mode

한 프로세스 내에서만 사용가능

Mutex

POSIX 함수

한 프로세스 내에서만 사용가능

하나의 공유자원 동기화에 사용

kernel-mode

프로세스끼리도 사용가능

타임아웃기능 사용가능

하나의 공유자원 동기화에 사용

Semaphore

kernel-mode

POSIX 함수

프로세스끼리도 사용가능

다중 공유자원 동기화에 사용

kernel-mode

프로세스끼리도 사용가능

타임아웃기능 사용가능

다중 공유자원 동기화에 사용

Condition Variable

POSIX 함수

타임아웃기능 사용가능

다중 스레드를 동시에 깨울 수 있음

없음

Event

없음

kernel-mode

프로세스끼리도 사용가능

타임아웃기능 사용가능

다중 스레드나 프로세스를 동시에

깨울 수 있음

여기서는 표시된 가장 많이 사용되는 객체들만 정리하기로 함.


 

0. 사전지식

 

User-mode 객체

 

커널 영역의 객체를 접근할 필요 없이 메모리 상에서 객체를 생성하여 운용하므로 연산이 빠르다는 장점이 있지만 프로세스 간의 동기화는 할 수 없다.

 


Kernel-mode 객체

 

커널 객체를 사용한 동기화는 signaled, nonsignaled 둘 중 하나의 상태로 존재한다.

mutex, semaphore, event의 경우 생성할 때에는 signaled-mode이지만, 대기함수인

WaitForSingleObject() WaitForMultipleObject()를 사용하여 nonsignaled-mode로 만들 수 있다.

 

- signaled       

thread가 해당 객체에 접근이 가능하며, 접근한 thread는 객체를 사용 가능하다.

 

- nonsignaled  

thread는 해당 객체에 접근할 수 없고 signaled될 때까지 대기한다.


※ 관련용어

 

- critical section           

임계영역. 공유 자원을 접근하는 프로세스 내부의 코드 영역.

동기화 객체와 같은 단어지만 그 뜻은 다르다.

 

- deadlock                  

교착상태. 두 개 이상의 프로세스들이 더 이상 진행을 할 수 없는 상태.

 

- livelock                              

라이브락. 두 개 이상의 프로세스들이 다른 프로세스의 상태 변화에 따라 자신의 상태를 변화시

키는 작업만을 수행하고 있는 상태.

 

- mutual exclusion        

상호배제. 한 프로세스가 공유 자원을 접근하는 임계영역 코드를 수행하고 있으면 다른 프로세스

들은 공유 자원을 접근하는 임계영역 코드를 수행 할 수 없는 조건.

 

- race condition                      

경쟁상태. 두 개 이상의 프로세스가 공유 자원을 동시 접근하려는 상태.

 

- starvation                

기아. 특정 프로세스가 수행 가능한 상태임에도 불구하고 매우 오랜 기간 동안 자원을 사용하지

못하는 상태.



1. MUTEX

- 모든 thread에 사용되는 kernel-mode 동기화 객체.
- 공유자원에 대해 한 thread가 단독으로 점유하며 다른 thread의 접근을 차단.


뮤텍스 생성

HANDLE CreateMutex ( LPSECURITY_ATTRIBUTES lpMutexAttributes,

                      BOOL bInitialOwner,

                      LPCTSTR lpName );

 

뮤텍스 소유

HANDLE OpenMutex ( DWORD dwDesiredAccess,

                     BOOL bInheritHandle,

                     LPCTSTR lpName );

 

뮤텍스 소유 해제

BOOL ReleaseMutex ( HANDLE hMutex );

 

뮤텍스 삭제

HRESULT CloseHandle ( HANDLE hHandle );


- 공유자원을 다 사용한 후 ReleaseMutex를 호출하여 다른 thread의 접근을 허용.

* critical section은 구조체의 값을 통해 잠금을 하지만 mutex는 객체를 생성하기 때문에 critical section보다 느리다.

* mutex name을 가지며 name은 유일한 값이 된다.

* mutex
를 소유한 thread mutex의 소유를 반납하지 않고 비정상 종료될 경우 강제로 해제시켜 다른 thread에서 소유할 수 있도록 한다.

* mutex
를 소유한 thread가 중복으로 호출할 경우 critical section처럼 진입을 허용하고 내부
count만 증가시켜 deadlock을 발생시키지 않는다.

* mutex
는 다른 말로 상호배제 semaphore라고도 불리는데 이는 mutex가 특별한 형태의 
binary semaphore이다. (일반 binary semaphore와 다른 이유는 소유권이 있다는 점이다)


 

2. SEMAPHORE


- mutex
와 비슷하지만 접근 가능한 thread의 개수를 지정 가능한 kernel-mode 동기화 객체.

- 내부 count만큼 thread가 접근가능하며 count 0이 되었을 때는 다른 thead의 접근을 차단.
- ReleaseSemaphore를 호출하여 count 1 증가시켜 다른 thread의 접근을 허용

세마포어 생성

HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

                           LONG lInitialCount,

                           LONG lMaximumCount,

                           LPCTSTR lpName );

 

세마포어 진입

HANDLE OpenSemaphore ( DWORD dwDesiredAccess,

                          BOOL bInheritHandle,

                          LPCTSTR lpName );

 

세마포어 진입해제

BOOL ReleaseSemaphore ( HANDLE hSemaphore,

                         LONG lReleaseCount,
                        
LPLONG lpPreviousCount );

 

세마포어 삭제

HRESULT CloseHandle ( HANDLE hHandle );


* mutex
와 마찬가지로 named semaphore는 프로세스 간 동기화도 가능

* thread process에 무관하게 진입 시 count를 증가

* 하나의 thread에서 모두 소유한 상태에서 다시 진입하면 deadlock이 발생

* binary semaphore
0이나 1의 값을 가지는 semaphore 0이면 사용불가, 1이면 사용 가능하다.

* counting semaphore
내부 카운터를 사용하여 여러 차례 획득하고 반환할 수 있는 semaphore로 생성 시 초기 Token값을 지정할 수 있다.



3. CRITICAL SECTION

 

- 같은 프로세스 내에서만 사용될 수 있는 user-mode 동기화 객체.
- 공유자원에 대해 한 thread가 단독으로 점유하며 다른 thread의 접근을 차단.

- 공유자원을 다 사용한 후 LeaveCriticalSection을 호출하여 다른 thread의 접근을 허용.

크리티컬 섹션의 초기화

void InitializeCriticalSection ( LPCRITICAL_SECTION lpCriticalSection );

 

크리티컬 섹션에 진입

void EnterCriticalSection ( LPCRITICAL_SECTION lpCriticalSection );

 

크리티컬 섹션에 진입해제

void LeaveCriticalSection ( LPCRITICAL_SECTION lpCriticalSection );

 

크리티컬 섹션을 삭제

void DeleteCriticalSection ( LPCRITICAL_SECTION lpCriticalSection );

 

Spin Lock Count를 설정.

BOOL InitializeCriticalSectionAndSpinCount ( LPCRITICAL_SECTION lpCriticalSection,

                                                      DWORD dwSpinCount );


* kernel-mode에 비해 빠르지만 한 프로세스 안에서만 사용이 가능하다. (mutex보다 2~10)

  대기 thread가 많을 경우, kernel-mode로 대기 thread를 관리하므로 user-mode일 때만 빠름.

 

* 유저영역 메모리에 존재하는 구조체이므로 한 process에 속한 thread들의 동기화만 가능하다.

 

* 하나의 thread에서 여러 번 호출할 때에는 이를 무시한다. (deadlock 방지를 위해)

 

* mutex와 달리 소유한 thread가 비정상 종료하면 다른 thread들은 무한정 대기한다.



critical section의 동작


1) thread
EnterCriticalSection()으로 진입하는 경우, CRITICAL_SECTION 구조체 내부의 변수를 검사하여 off이면 on으로 변경한 후 작업을 진행. (user-mode에서 이루어짐)

2) thread가 진입한 경우, single-CPU상에서는 kernel-mode로 넘어가 동기화 작업이 수행되고, multiple-CPU상에서는 Spin Count만큼 busy-waiting하며 lock해제를 대기.

3) kernel-mode로 넘어가면 해당 thread semaphore를 이용한 wait상태가 됨.

4) thread LeaveCriticalSection()으로 진입 해제하면 CRITICAL_SECTION 구조체 내부의 변수를 off로 변경하고, semaphore를 이용한 경우에는 기다리고 있는 thread에게 통지를 함.

5) EnterCriticalSection()으로 진입한 thread에서 EnterCriticalSection()을 다시 한번 호출하면, 내부적으로 count 변수 값을 올려서 deadlock을 막음. (EnterCriticalSection()을 호출한 횟수만큼 LeaveCriticalSection()을 해야 함)


 

Spin Lock

 

위에서처럼 critical section multiple-CPU상에서 critical section을 사용할 수 없는 경우,

kernel-mode semaphore를 사용하여 대기중인 thread를 관리하게 된다.

 

semaphore critical section을 다른 thread에서 사용 중이라면 context-switching을 하여

CPU의 소유권을 반납하게 되고 다시 자신의 차례가 되면 다시 critical section의 사용여부를

조사하고, critical section을 아직 사용 중이라면 다시 위의 동작을 반복하며 대기(busy waiting)

하게 된다.

 

이러한 빈번한 context-switch를 방지하기 위해 일정시간 동안 critical section의 사용이

끝나기를 기다리게 하는 것이 spin lock이다.

 

Context Switch

 

CPU가 다른 프로세스로 교환하기 위해 이전의 프로세스의 상태를 보관하고, 새로운 프로세스의

보관된 상태를 적재하는 작업으로 보통 process context switch thread context switch보다

시간이 더 소요된다.


 

4. EVENT

 

- 특정 상황이 발생했을 때 이를 알리기 위해 사용하는 kernel-mode 동기화 객체.

- mutex, semaphore, critical section은 공유자원에 대한 보호에 사용되지만, event는 주로 thread의 작업 우선순위나 작업시기의 조정을 위해 쓰임

이벤트 생성

HANDLE OpenEvent ( DWORD dwDesiredAccess,

 BOOL bInheritHandle,

LPCTSTR lpName );

 

이벤트 지정

BOOL SetEvent ( HANDLE hEvent );

 

이벤트 해제

BOOL ResetEvent ( HANDLE hEvent );

 

이벤트 삭제

HRESULT CloseHandle ( HANDLE hHandle );


* 윈도우 시스템에서만 존재하는 동기화 객체이다.

 

* 다른 객체들은 thread가 진입하게 되면 nonsignaled 상태로 자동으로 변경되게 된다.
이를 자동리셋모드 (auto-reset mode)라고 하는데 event는 자동리셋모드는 물론 수동리셋모드(manual-reset mode)도 사용할 수 있다.

posted by 어린왕자악꿍