1

나는이 문제에 대해 조언 해 주시면 고맙겠습니다.외부 세계와 스레드 안전 방식으로 데이터 멤버를 공유

class Foo 
{ 
    TData data; 
public: 
    TData *getData() { return &data; } // How can we do this in a thread safe manner ? 
}; 

따라서 스레드로부터 안전하도록 getData() 메커니즘을 만들고 싶습니다. 나는 그것에 대한 액세스를 동기화하는 데 사용되는 뮤텍스와 함께 다음 템플릿 클래스에 데이터 멤버를 패키징하는 자체 솔루션을 생각해 냈습니다. 어떻게 생각해 ? 가능한 문제는 무엇입니까?

class locked_object : boost::noncopyable 
{ 
    T *object; 
    TLockable *lock; 
    bool locked; 

public: 
    locked_object(T *_object, TLockable *_lock) : object(_object), lock(_lock), locked(true) 
    { 
     lock->lock(); 
    } 

    ~locked_object() 
    { 
     lock->unlock(); 
    } 

    T *get() 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 
     return this->tobject; 
    } 


    void unlock() 
    { 
     locked = false; 

     lock->unlock(); 
    } 

    T *operator ->() const 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 

     return this->tobject; 
    } 

    operator T *() const 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 

     return this->tobject; 
    } 
}; 

미리 의견을 보내 주셔서 감사합니다.

afat

+0

들여 쓰기 된 코드 블록에서'& gt' (어쨌든';'가 누락 됨)을 쓸 필요가 없습니다. '> '라고 써주십시오. –

+0

복사본을 구할 수 있다면 Alexandrescu의 Modern C++ Design *을보십시오. –

답변

2

혹시 The Law of Demeter에 대해 들어 보셨습니까?

비슷한 조언 (셔터에서 나는 생각한다)가있다 : 이 당신의 내부에 대한 참조를 공유하지 마십시오 모두가 때문에 내부에 대한 참조를 공유함으로써,을 결합하지 않도록하기위한 것입니다

는, 그 의미는 공용 인터페이스가 구현 세부 사항을 유출했는지 확인하십시오.

이제 인터페이스가 작동하지 않습니다.

문제

당신이 아닌 객체를 프록시를 잠그는 것입니다 : 나는 아직도 여러 경로를 통해 액세스 할 수 있습니다 : Foo에서

  • , 필요하지 뮤텍스를 -> oups?
  • 두 가지가 다릅니다. locked_object -> 의도적으로 보이지 않습니다.

일반적으로 개체의 단일 부분을 잠글 수 없습니다. 개체 전체에 트랜잭션 의미론을 사용할 수 없으므로 더 이상 중요하지 않습니다.

+0

Demeter/L.K.P.의 법칙에 +1. –

1

이것은 특별히 안전하지 않습니다.

물론
locked_object<T> o(...); 
T* t = o.get(); 
o.unlock(); 
t->foo(); // Bad! 

, 위의 코드가 나쁜 이유를 쉽게 알 수 있지만, 실제 코드는 훨씬 더 복잡하고있는 포인터를 걸 수있는 많은 방법이있다 : 아무것도 순서가 점점 것들에서 사용자를 중지하지 자물쇠가 풀려 난 후 핀 고정이 훨씬 어려워집니다.

2

정확한 시간에 물체가 잠기고 잠기지 않도록 사용자가 디자인을 꼼꼼히 따릅니다. 잠긴 객체가 오류 검사를 수행한다고해도 모든 객체를 포함하지는 않습니다 (객체를 끝낼 때 객체를 놓는 것을 잊어 버리는 것과 같습니다)

안전하지 않은 객체 TData이 있다고합시다. Foo에 랩핑하지만 Foo 대신 포인터를 TData에 반환하고 TData에있는 모든 public 메소드를 Foo에 다시 구현하지만 잠금 및 잠금 해제를 사용합니다.

이것은 인터페이스가 구현을 호출하기 전에 잠금을 추가한다는 점을 제외하고는 pImpl 패턴과 매우 유사합니다. 이렇게하면 사용자는 객체가 스레드 안전성을 가지며 동기화에 대해 걱정할 필요가 없다는 것을 알 수 있습니다.

2

이것은 멀티 스레딩의 핵심 문제이며 클라이언트 코드가 스레드로부터 안전하게 개체를 사용하도록 요구할 수 없습니다. 클라이언트 코드가 성공의 구덩이에 빠지게 할 수있는 일도 실제로 할 수 없으며, 스스로 잠금을 처리해야합니다. 코드를 작성하는 데 부담을 가하는 일은 가장 적게 일어날 가능성이있는 사람에게 작업하는 것입니다.

복사본의 복사본을 접근 자로 반환하면 더 쉽게 만들 수 있습니다. 스레드로부터 안전하며 하나의 스레드가 소유 한 복사본이 하나뿐입니다. 객체를 수정하면 원하는 결과를 얻지 못할 것이라는 것을 다시 알기 위해 사본 유형을 변경 불가능하게해야합니다. 나쁘게 물지 않을 수없는 부작용은이 사본이 정의 상 부실하다는 것입니다. 이것들은 유익보다 해를 입힐 수있는 반창고입니다.

클라이언트 프로그래머가 무엇을 해야할지 알 수 있도록 메소드의 내용을 문서화하십시오.