귀속 가능 자물쇠와 비귀속 자물쇠

4361 단어
귀속 가능 자물쇠와 비귀속 자물쇠
개술
가장 흔히 볼 수 있는 프로세스/스레드의 동기화 방법으로는 상호 배척 자물쇠(또는 상호 배척 Mutex), 읽기 자물쇠(rdlock), 조건 변수(cond), 신호량(Semophore) 등이 있다.Windows 시스템에서는 임계 영역(Critical Section)과 이벤트 객체(Event)도 자주 사용하는 동기화 방법입니다.
 
간단하게 말하면 서로 밀어냄 자물쇠가 하나의 임계 구역을 보호하는데 이 임계 구역에서 한 번에 최대 한 라인만 들어갈 수 있다.만약 여러 프로세스가 같은 임계 구역 내에서 활동한다면, 경쟁 조건 (race condition) 이 오류를 초래할 수 있습니다.
 
읽기 자물쇠는 넓은 의미의 논리적으로도 공유판의 상호 배척 자물쇠라고 볼 수 있다.만약에 임계 구역의 대부분이 읽기 조작이고 소량의 쓰기 조작만 있다면 읽기 자물쇠는 어느 정도에 라인이 서로 밀어내는 대가를 낮출 수 있다.
 
조건 변수는 라인이 경쟁 없이 어떤 조건의 발생을 기다릴 수 있도록 허용한다.이 조건이 발생하지 않았을 때, 라인은 줄곧 휴면 상태에 있을 것이다.다른 스레드 알림 조건이 발생했을 때, 스레드는 깨어나서 계속 아래로 실행됩니다.조건 변수는 밑바닥의 동기화 원어를 비교하는 것으로 직접 사용하는 경우가 많지 않아 고위층 간의 라인 동기화를 실현하는 데 종종 사용된다.조건 변수를 사용하는 대표적인 예는 스레드 풀(Thread Pool)입니다.
 
운영체제의 프로세스 동기화 원리를 배울 때 가장 많이 말하는 것은 신호량이다.신호량의 PV 조작을 정성껏 설계함으로써 매우 복잡한 프로세스 동기화 상황(예를 들어 고전적인 철학가의 식사 문제와 이발소 문제)을 실현할 수 있다.현실적인 프로그램 설계에서는 신호량을 사용하는 사람이 극히 적다.신호량으로 해결할 수 있는 문제는 신호량 대신 더 뚜렷하고 간결한 디자인 수단을 쓸 수 있을 것 같다. 
 
1  
1.1  

모든 스레드 동기화 방법 중, 아마도 상호 배척 잠금 (mutex) 의 출전률은 다른 방법보다 훨씬 높을 것이다.상호 배척 자물쇠의 이해와 기본적인 사용 방법은 모두 쉬우니 여기서는 더 이상 소개하지 않겠습니다.
Mutex는 반복 잠금(recursive mutex)과 비반복 잠금(non-recursive mutex)으로 나눌 수 있다.컴포지팅 자물쇠는 컴포지팅 자물쇠(reentrant mutex)라고도 할 수 있고, 컴포지팅 자물쇠가 아니면 컴포지팅 자물쇠(non-reentrant mutex)라고도 부른다.
양자의 유일한 차이점은 같은 라인에서 같은 귀속 자물쇠를 여러 번 얻을 수 있어 사라진 자물쇠가 생기지 않는다는 것이다.한 라인이 같은 비귀속 자물쇠를 여러 번 가져오면 사라진 자물쇠가 생긴다.
Windows의 Mutex 및 Critical Section은 재귀속적입니다.Linux의 pthreadmutex_t 자물쇠는 기본적으로 비귀속적입니다.표시할 수 있는 설정 PTHREADMUTEX_RECURSIVE 속성, pthreadmutex_t를 귀속 자물쇠로 설정합니다.
대부분의 상호 배척량을 어떻게 사용하는지 소개하는 문장과 책에서 이 두 개념은 종종 소홀히 되거나 얼렁뚱땅 넘어가 많은 사람들이 이 개념을 전혀 모른다.그러나 이 두 가지 자물쇠를 잘못 사용하면 프로그램의 자물쇠가 사라질 가능성이 높다.다음 순서를 보십시오.
MutexLock mutex;  
  •   

  • void foo()  
  • {  

  •     mutex.lock();  
  •     // do something  

  •     mutex.unlock();  
  • }  

  •   
  • void bar()  

  • {  
  •     mutex.lock();  

  •     // do something  
  •     foo();  

  •     mutex.unlock();   
  • }  

  • foo 함수와bar 함수는 모두 같은 자물쇠를 얻었고,bar 함수는foo 함수를 호출합니다.만약 MutexLock 자물쇠가 비귀속 자물쇠라면, 이 프로그램은 즉시 사라집니다.따라서 프로그램에 자물쇠를 채울 때 각별히 조심해야 한다. 그렇지 않으면 이런 호출 관계로 인해 자물쇠가 사라지기 쉽다.
    요행 심리는 존재하지 마라. 이런 상황은 매우 드물게 나타난다.코드가 어느 정도 복잡하고 여러 사람이 관리하며 호출 관계가 복잡할 때 프로그램에서 이런 오류를 범하기 쉽다.다행히 이런 원인으로 인한 자물쇠는 쉽게 제거된다.
    그러나 이것은 비귀속 자물쇠를 귀속 자물쇠로 대체해야 한다는 것을 의미하지는 않는다.귀속 자물쇠를 사용하는 것은 물론 간단하지만, 왕왕 일부 코드 문제를 숨길 수 있다.예를 들어 호출 함수와 호출된 함수는 자신이 자물쇠를 얻었다고 생각하고 같은 대상을 수정하는데 이때 문제가 생기기 쉽다.따라서 비귀속 자물쇠를 사용할 수 있는 상황에서 가능한 한 비귀속 자물쇠를 사용해야 한다. 왜냐하면 사라진 자물쇠는 상대적으로 디버깅을 통해 발견하기 쉽기 때문이다.프로그램 설계에 문제가 있으면 일찍 노출될수록 좋다.
     
    1.2  

    AUPE v2라는 책은 이러한 상황에서 발생하는 고정 자물쇠를 피하기 위해 12장에서 설계 방법을 제시했다.즉, 함수가 잠긴 상태에서 사용할 수도 있고, 잠기지 않은 상태에서 사용할 수도 있으며, 이 함수를 두 가지 버전으로 나눌 수도 있다. 잠금 버전과 잠금 버전(nolock 접미사 추가)이다.
    예를 들어foo() 함수를 두 함수로 분해한다.
    //잠금 해제 버전 4
  • void foo_nolock()  

  • {  
  •     // do something  

  • }  
    4
  • //잠금 버전 4
  • void fun()  
  • {  

  •     mutex.lock();  
  •     foo_nolock();  

  •     mutex.unlock();  
  • }  

  • 인터페이스의 장래 확장성을 위해bar() 함수를 같은 방법으로bar 로 분해할 수 있습니다without_lock() 함수와bar() 함수.
    Douglas C. Schmidt(ACE 프레임워크의 주요 작성자)의 "Strategized Locking, Thread-safe Interface, and Scoped Locking"논문에서 C++ 기반의 스레드 보안 인터페이스 모델(Thread-safe interface pattern)을 제시했는데 AUPE의 방법과 공통점이 있다.즉 인터페이스를 설계할 때 모든 함수도 두 개의 함수로 분해된다. 자물쇠를 사용하지 않은 함수는private나protected 유형이고 자물쇠를 사용하는 함수는public 유형이다.인터페이스는 다음과 같습니다.
    class T  
  • {  

  • public:  
  •     foo();//자물쇠 추가
  •     bar();//자물쇠 추가
  • private:  

  •     foo_nolock();  
  •     bar_nolock();  

  • }  
    대외 인터페이스인public 함수는 자물쇠가 없는 사유 변수 함수만 호출할 수 있고 서로 호출할 수 없습니다.함수의 구체적인 실현에 있어서 이 두 가지 방법은 기본적으로 같다.
    위에서 말한 두 가지 방법은 통상적인 상황에서는 문제가 없으며, 자물쇠가 사라지는 것을 효과적으로 피할 수 있다.그러나 일부 복잡한 리셋 상황에서는 반드시 귀속 자물쇠를 사용해야 한다.예를 들어foo 함수는 외부 라이브러리의 함수를 호출하고 외부 라이브러리의 함수는bar() 함수를 리셋합니다. 이 때 반드시 귀속 자물쇠를 사용해야 합니다. 그렇지 않으면 자물쇠가 사라집니다.

    좋은 웹페이지 즐겨찾기