programing

뮤텍스는 변경 가능해야합니까?

randomtip 2021. 1. 15. 08:07
반응형

뮤텍스는 변경 가능해야합니까?


이것이 스타일 문제인지 또는 어려운 규칙이 있는지 확실하지 않습니다.

공용 메서드 인터페이스를 가능한 한 const로 유지하고 싶지만 개체 스레드를 안전하게 만들고 싶다면 변경 가능한 뮤텍스를 사용해야합니까? 일반적으로이 스타일이 좋은가요, 아니면 const가 아닌 메소드 인터페이스가 선호되어야합니까? 당신의 견해를 정당화하십시오.


[ 답변 수정 ]

기본적으로 변경 가능한 뮤텍스와 함께 const 메서드를 사용하는 것은 최소한 객체를 수정하지 않음을 나타 내기 위해 참조를 반환하지 말고 값으로 반환해야합니다. 뮤텍스는 const가 아니어야합니다. 잠금 / 잠금 해제 메서드를 const로 정의하는 것은 뻔뻔한 거짓말입니다.

실제로 이것 (및 메모 화)은 내가 본 mutable키워드 의 유일한 공정한 사용 입니다.

객체 외부에있는 뮤텍스를 사용할 수도 있습니다. 모든 메소드를 재진입 할 ​​수 있도록 정렬하고 사용자 { lock locker(the_mutex); obj.foo(); }직접 잠금을 관리하도록 합니다. 입력하기가 어렵지 않습니다.

{
    lock locker(the_mutex);
    obj.foo();
    obj.bar(42);
    ...
}

두 개의 뮤텍스 잠금이 필요하지 않은 장점이 있습니다 (그리고 객체의 상태가 변경되지 않았 음을 보장합니다).


숨겨진 질문은 : 클래스를 보호하는 뮤텍스를 어디에 두는가?

요약하자면 뮤텍스로 보호되는 객체의 내용을 읽고 싶다고 가정 해 보겠습니다.

"read"메소드는 객체 자체를 변경하지 않기 때문에 의미 상 "const"여야합니다. 그러나 값을 읽으려면 뮤텍스를 잠그고 값을 추출한 다음 뮤텍스를 잠금 해제해야합니다. 즉, 뮤텍스 자체를 수정해야하므로 뮤텍스 자체가 "const"가 될 수 없습니다.

뮤텍스가 외부인 경우

그럼 다 괜찮아. 객체는 "const"일 수 있으며 뮤텍스는 다음과 같을 필요가 없습니다.

Mutex mutex ;

int foo(const Object & object)
{
   Lock<Mutex> lock(mutex) ;
   return object.read() ;
}

IMHO, 이것은 나쁜 해결책입니다. 왜냐하면 누구나 다른 것을 보호하기 위해 뮤텍스를 재사용 할 수 있기 때문입니다. 당신을 포함 해서요 사실, 코드가 충분히 복잡하면이 뮤텍스가 정확히 무엇을 보호하는지 혼란스러워 할 것이기 때문에 자신을 배신 할 것입니다.

알아요 : 저는 그 문제의 희생자였습니다.

뮤텍스가 내부 인 경우

캡슐화를 위해 뮤텍스를 보호하는 개체에서 가능한 한 가깝게 배치해야합니다.

일반적으로 내부에 뮤텍스가있는 클래스를 작성합니다. 그러나 조만간 복잡한 STL 구조 나 내부에 뮤텍스없이 다른 사람이 작성한 모든 것을 보호해야합니다 (좋은 일입니다).

이를 수행하는 좋은 방법은 뮤텍스 기능을 추가하는 상속 템플릿으로 원본 객체를 파생하는 것입니다.

template <typename T>
class Mutexed : public T
{
   public :
      Mutexed() : T() {}
      // etc.

      void lock()   { this->m_mutex.lock() ; }
      void unlock() { this->m_mutex.unlock() ; } ;

   private :
      Mutex m_mutex ;
}

이렇게하면 다음과 같이 작성할 수 있습니다.

int foo(const Mutexed<Object> & object)
{
   Lock<Mutexed<Object> > lock(object) ;
   return object.read() ;
}

문제는 objectconst 이기 때문에 작동하지 않고 잠금 객체가 non-const lockunlock메서드를 호출한다는 것 입니다.

딜레마

const비트 단위 const 객체로 제한 된다고 생각 하면 실수를하게되며 "외부 뮤텍스 솔루션"으로 돌아 가야합니다.

해결책은 const( volatile클래스의 메서드 한정자로 사용될 때 와 마찬가지로) 더 의미 론적 한정자 임을 인정하는 것 입니다 . 클래스가 완전하지 않다는 사실을 숨기고 const있지만 const메서드를 호출 할 때 클래스의 의미있는 부분이 변경되지 않을 것이라는 약속을 지키는 구현을 제공해야합니다 .

그런 다음 뮤텍스 변경 가능 및 잠금 / 잠금 해제 메소드를 선언해야합니다 const.

template <typename T>
class Mutexed : public T
{
   public :
      Mutexed() : T() {}
      // etc.

      void lock()   const { this->m_mutex.lock() ; }
      void unlock() const { this->m_mutex.unlock() ; } ;

   private :
      mutable Mutex m_mutex ;
}

The internal mutex solution is a good one IMHO: Having to objects declared one near the other in one hand, and having them both aggregated in a wrapper in the other hand, is the same thing in the end.

But the aggregation has the following pros:

  1. It's more natural (you lock the object before accessing it)
  2. One object, one mutex. As the code style forces you to follow this pattern, it decreases deadlock risks because one mutex will protect one object only (and not multiple objects you won't really remember), and one object will be protected by one mutex only (and not by multiple mutex that needs to be locked in the right order)
  3. The mutexed class above can be used for any class

So, keep your mutex as near as possible to the mutexed object (e.g. using the Mutexed construct above), and go for the mutable qualifier for the mutex.

Edit 2013-01-04

Apparently, Herb Sutter have the same viewpoint: His presentation about the "new" meanings of const and mutable in C++11 is very enlightening:

http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/

ReferenceURL : https://stackoverflow.com/questions/4127333/should-mutexes-be-mutable

반응형