Search

'critical_section'에 해당되는 글 1건

  1. 2010.08.15 Concurrency Runtime – 동기화 객체 1. ( critical_section & reader_writer_lock )

Concurrency Runtime
– 동기화 객체 1. ( critical_section & reader_writer_lock )

작성자: 임준환( mumbi at daum dot net )

 

시작하는 글

 기본적으로 Asynchronous Agents Library( 이하 AAL ) 에서 제공하는 message block 들은 스레드에 안전합니다. 이 말은 내부적으로 동기화를 구현하고 있다는 말입니다.

 하지만 Concurrency Runtime 을 이용한 동시 프로그래밍을 할 때 AAL 의 message block 을 사용하지 않고 일반적인 사용자 변수들을 사용할 수 있습니다. 하지만 이런 변수들은 동기화가 되지 않아 직접 동기화를 해 주어야 합니다.

 이 때 Windows API 의 유저 모드 혹은 커널 모드의 동기화 객체를 사용할 수 있지만, Concurrency Runtime 에서 제공하는 동기화 객체를 쉽게 사용할 수 있습니다.

 이번 글에서는 Concurrency Runtime 에서 제공하는 동기화 객체들에 대해서 알아보도록 하겠습니다.

 

critical_section

 임계 영역을 의미하는 객체로, 어떤 영역을 잠그고 풀 수 있습니다. 잠긴 영역에 도달한 다른 스레드는 잠김이 풀릴 때까지 기다려야 합니다. 그러므로 해당 영역을 모두 사용한 후에는 다른 스레드도 사용할 수 있도록 풀어주어야 합니다.

 이 기능은 Windows API 에서 제공하는 유저 모드 동기화 객체인 CRITICAL_SECTION 과 같은 기능을 합니다.

 

멤버 함수

 생성자와 소멸자를 제외한 public 인 멤버 함수들에 대해서 알아보겠습니다.

 

void lock()

 해당 영역을 잠급니다. 잠근다는 의미는 영역의 접근 권한을 획득한다는 의미도 있습니다. 잠긴 영역에 도달한 다른 스레드들은 잠김이 풀릴 때까지 기다려야 합니다.

 

bool try_lock()

 해당 영역의 접근을 시도합니다. 이미 다른 스레드에 의해 잠겨있더라도 풀릴 때까지 기다리지 않고 반환합니다.

 이미 다른 스레드에 의해 잠겨있다면 false 를, 아니면 true 를 반환합니다.

 

void unlock()

 접근 영역의 접근 권한을 반환합니다. 즉, 잠금을 해제합니다. 이 함수가 호출된 후에 다른 스레드가 이 영역에 접근할 수 있습니다.

 

native_handle_type native_handle()

 native_handle_typecritical_section 의 참조입니다. 결국, 이 함수는 자기 자신의 참조를 반환합니다.

 

범위 잠금

 범위 잠금이란 코드 내의 블럭의 시작부터 블럭이 끝날 때까지 잠그는 것을 말합니다. 이것은 생성자에서 잠그고, 소멸자에서 잠금을 해제하는 원리( RAII – Resource Acquisition Is Initialization )로 구현되어 있으며, 굳이 unlock() 을 호출하지 않아도 된다는 편리함이 있습니다.

 

critical_section::scoped_lock

 critical_section 객체를 범위 잠금 객체로 사용합니다. 생성자로 critical_section 의 참조를 넘겨주어야 합니다. 스택에 할당되고 가까운 코드 블럭이 끝날 때, 소멸자가 호출되어 잠금이 해제됩니다.

 

reader_writer_lock

 reader_writer_lock 은 쓰기 접근 권한과 읽기 접근 권한을 구분하여 획득하고 잠글 수 있는 객체입니다.

한 스레드가 쓰기 접근 권한을 획득하여 잠긴다면, 다른 스레드의 모든 쓰기 / 읽기 접근 권한 획득시도가 실패하며 기다리게 됩니다.

 만약 한 스레드가 읽기 접근 권한을 획득하여 잠긴다면, 다른 스레드가 읽기 접근 권한 획득 시도 시, 동시에 읽기 접근 권한을 획득 가능합니다.

 위에 설명한 것처럼 2 가지 종류의 접근 권한이 있지만, 잠기는 것은 단 하나의 객체라는 것을 명심하셔야 합니다. 이 사실을 잊으면 헷갈릴 수 있습니다.

 reader_writer_lock 의 이와 같은 기능은 Windows API 의 SRW( Slim Reader Writer ) 잠금과 유사합니다. 다른 점이 있다면 reader_writer_lock 은 기본 잠금이 쓰기 잠금으로 되어 있고, 잠겼을 때 다른 스레드들이 기다린 순서대로 접근 권한을 얻는다는 것입니다. 즉, Windows SRW 는 기본 잠금이라는 개념이 없고, 기다린 스레드들 중 어떤 순서로 접근 권한을 얻는지 알 수 없습니다.

 

멤버 함수

 생성자와 소멸자를 제외한 public 인 멤버 함수들에 대해서 알아보겠습니다.

 

void lock()

 기본 잠금으로 쓰기 접근 권한을 획득하고 잠급니다. 이미 다른 스레드에서 접근 권한을 획득하여 잠겨 있다면 해제될 때까지 기다립니다.

 이 함수로 잠길 경우, lock() 과 읽기 접근 권한 획득 함수인 lock_read() 으로 접근 권한을 획득하려 하더라도 획득할 수 없고, 잠금이 해제될 때까지 기다리게 됩니다.

 

bool try_lock()

 쓰기 접근 권한 획득을 시도합니다. 이미 다른 스레드가 접근 권한을 획득하여 잠겼을 경우에는 false 를 반환하고, 접근 권한을 획득하게 되면 true 를 반환합니다. 즉, 대기하지 않습니다.

 

void lock_read()

 읽기 잠금으로 읽기 접근 권한 획득을 합니다. 다른 스레드에서 lock_read() 로 읽기 접근 권한 획득을 시도하면 이미 다른 스레드가 읽기 접근 권한을 획득하였다고 하더라도 동시에 읽기 접근 권한을 획득할 수 있습니다.

 다른 스레드에서 lock() 으로 쓰기 접근 권한 획득을 시도한다면, 이 스레드는 모든 읽기 권한으로 인한 잠금이 해제될 때까지 기다리게 됩니다.

 

bool try_lock_read()

 읽기 접근 권한 획득을 시도합니다. 이미 다른 접근 권한을 획득하여 잠겼을 경우에는 false 를 반환하고, 접근 궈한을 획득하게 되면 true 를 반환합니다. 즉, 대기하지 않습니다.

 

void unlock()

 어떤 잠금이든 해제합니다.

 

범위 잠금

 critical_section 과 마찬가지로 범위 잠금을 지원합니다.

 

reader_writer_lock::scoped_lock

 critical_section 과 같습니다. 다른 점이 있다면 이 잠금 객체는 쓰기 잠금의 범위 잠금 객체입니다.

 

reader_writer_lock:scoped_lock_read

 이 잠금 객체는 읽기 잠금의 범위 잠금 객체입니다.

 

마치는 글

 Concurrency Runtime 에서 제공하는 동기화 객체은 critical_sectionreader_writer_lock 에 대해서 알아보았습니다.

 reader_writer_lock 이 동시 읽기 접근이 가능하므로 critical_section 보다는 상황에 따라 성능이 좋을 수 있습니다.

 다음 글에서는 동기화 객체 중 마지막 하나, event 에 대해서 알아보도록 하겠습니다.