2010-04-02 3 views
1

내가 이런 부스트 뮤텍스와 잠금 (단지 관련 부분)를 사용하는 사용자 정의 클래스가 : 나는 동일한 스레드에서 개체 번 이상을 고정하려고하면왜 boost :: recursive_mutex가 예상대로 작동하지 않습니까?

template<class T> class FFTBuf 
{ 
    public: 
     FFTBuf(); 
     [...] 
     void lock(); 
     void unlock(); 
    private: 
     T *_dst; 
     int _siglen; 
     int _processed_sums; 
     int _expected_sums; 
     int _assigned_sources; 
     bool _written; 
     boost::recursive_mutex _mut; 
     boost::unique_lock<boost::recursive_mutex> _lock; 
}; 

template<class T> FFTBuf<T>::FFTBuf() : _dst(NULL), _siglen(0), 
    _expected_sums(1), _processed_sums(0), _assigned_sources(0), 
    _written(false), _lock(_mut, boost::defer_lock_t()) 
{ 
} 

template<class T> void FFTBuf<T>::lock() 
{ 
    std::cerr << "Locking" << std::endl; 
    _lock.lock(); 
    std::cerr << "Locked" << std::endl; 
} 

template<class T> void FFTBuf<T>::unlock() 
{ 
    std::cerr << "Unlocking" << std::endl; 
    _lock.unlock(); 
} 

을, 나는 (예외가 lock_error) :

[email protected] $ ./src/test 
Locking 
Locked 
Locking 
terminate called after throwing an instance of 'boost::lock_error' 
    what(): boost::lock_error 
zsh: abort ./src/test 

왜 이런 우연은 다음과 같습니다

#include "fft_buf.hpp" 

int main(void) { 
    FFTBuf<int> b(256); 
    b.lock(); 
    b.lock(); 
    b.unlock(); 
    b.unlock(); 

    return 0; 
} 

는 출력 pening? 일부 개념을 잘못 이해하고 있습니까?

답변

2

이 시도 :

template<class T> void FFTBuf<T>::lock() 
{ 
    std::cerr << "Locking" << std::endl; 
    _mut.lock(); 
    std::cerr << "Locked" << std::endl; 
} 

template<class T> void FFTBuf<T>::unlock() 
{ 
    std::cerr << "Unlocking" << std::endl; 
    _mut.unlock(); 
} 

당신은 두 번 unique_lock _lock의 동일한 인스턴스를 사용하여이 문제입니다. 재귀 뮤텍스의 lock() 및 unock() 메서드를 직접 사용해야하거나 unique_lock의 두 인스턴스 (예 : _lock_lock_2)를 사용해야합니다.

업데이트

나는 당신의 클래스가 public 메소드 lock()unlock()을 가지고 있으며, 실제 프로그램에서 내 관점에서 나쁜 생각입니다 추가 할

. 또한 실제 프로그램에서 클래스의 구성원으로서 unique_lock을 갖는 것은 종종 나쁜 생각입니다.

+0

이 방법이 효과적이지만 그 이유는 무엇입니까? – Kjir

+0

그러면 boost :: unique_lock을 어떻게 사용합니까? 단순히 뮤텍스를 사용하면 어떤 장점이 있습니까? – Kjir

+2

RAII, 제 생각 엔. http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization –

3

자물쇠는 보호 된 자원의 일부가 아니어야하며 호출자가 한 명의 호출자를 가졌으므로 호출자의 일부 여야합니다. 그들은 다른 unique_lock을 사용해야합니다.

unique_lock의 목적은 뮤텍스를 RAII로 잠그고 해제하는 것입니다. 따라서 잠금 해제를 명시 적으로 호출 할 필요가 없습니다.

unique_lock이 메소드 본문 내에서 선언되면 호출 스레드 스택에 속하게됩니다.

그래서 더 올바른 사용은 다음과 같습니다 CONST 방법에 대한 뮤텍스에

#include <boost/thread/recursive_mutex.hpp> 
#include <iostream> 

template<class T> 
class FFTBuf 
{ 
public : 
    FFTBuf() 
    { 
    } 

    // this can be called by any thread 
    void exemple() const 
    { 
     boost::recursive_mutex::scoped_lock lock(mut); 
     std::cerr << "Locked" << std::endl; 

     // we are safe here 
     std::cout << "exemple" << std::endl ; 

     std::cerr << "Unlocking (by RAII)" << std::endl; 
    } 

    // this is mutable to allow lock of const FFTBuf 
    mutable boost::recursive_mutex mut; 
};  

int main(void) 
{ 
    FFTBuf<int> b ; 

    { 
     boost::recursive_mutex::scoped_lock lock1(b.mut); 
     std::cerr << "Locking 1" << std::endl; 

     // here the mutex is locked 1 times 

     { 
      boost::recursive_mutex::scoped_lock lock2(b.mut); 
      std::cerr << "Locking 2" << std::endl; 

      // here the mutex is locked 2 times 

      std::cerr << "Auto UnLocking 2 (by RAII) " << std::endl; 
     } 

     b.exemple(); 

     // here the mutex is locked 1 times 

     std::cerr << "Auto UnLocking 1 (by RAII) " << std::endl; 
    } 

    return 0; 
} 

참고 변경 가능.

부스트 뮤텍스 유형에는 좋은 unique_lock 유형 인 scoped_lock typedef가 있습니다.

4

이름에서 알 수 있듯이 뮤텍스는 recursive이지만 잠금은 아닙니다.

즉, 여기에 디자인 문제가 있습니다. 잠금 작업은 외부에서 액세스 할 수없는 것이 좋습니다.

class SynchronizedInt 
{ 
public: 
    explicit SynchronizedInt(int i = 0): mData(i) {} 

    int get() const 
    { 
    lock_type lock(mMutex); 
    toolbox::ignore_unused_variable_warning(lock); 

    return mData; 
    } 

    void set(int i) 
    { 
    lock_type lock(mMutex); 
    toolbox::ignore_unused_variable_warning(lock); 

    mData = i; 
    } 


private: 
    typedef boost::recursive_mutex mutex_type; 
    typedef boost::unique_lock<mutex_type> lock_type; 

    int mData; 
    mutable mutex_type mMutex; 
}; 

recursive_mutex의 주요 포인트는 당신이 어떤 경우에 서로를 호출 복잡한 작업이있는 경우 발생할 수있는 주어진 스레드의 체인 잠금을 허용하는 것입니다.예를 들어

,의 얻을 조정할 추가 할 수 있습니다 :

int SynchronizedInt::UnitializedValue = -1; 

int SynchronizedInt::get() const 
{ 
    lock_type lock(mMutex); 
    if (mData == UnitializedValue) this->fetchFromCache(); 
    return mData; 
} 

void SynchronizedInt::fetchFromCache() 
{ 
    this->set(this->fetchFromCacheImpl()); 
} 

경우 문제는 여기에있다?

  • get

    가 잠금을 획득 set
  • set 시도를 호출 fetchFromCache를 호출 mMutex
  • 의 잠금 ...

우리가 recursive_mutex을 가지고 있지 않은 경우를 획득, 이것은 실패 .

+0

문제는 포인터를 반환하는 함수가 있고 클래스 외부에서 수행 된 작업에서 동기화가 필요하다는 것입니다. 그래서 내가 lock과 unlock 메쏘드를 필요로하는 이유를 클래스 안에 넣어서 명시 적으로 boost :: * _ lock을 외부 코드에서 사용할 필요가 없게했습니다. 아마도 더 나은 디자인을 생각해야합니다 ... – Kjir

+0

항상 카운트 된 소유주를 자물쇠에 반환하여 파손을 보장 할 수있는 솔루션이 있습니다. –

+0

"_ 잠금 작업은 외부에서 액세스 할 수 없게되는 것이 더 나을 것입니다." "SynchronizedInt"를 증가해야한다면 어떻게해야합니까? – curiousguy

관련 문제