2010-04-02 85 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):

#include "fft_buf.hpp" 

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

    return 0; 
} 

这是输出:

[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 

这是为什么HAP 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;。

更新

我想补充一点,你的类有公共方法lock()unlock()和我在一个真正的程序的角度来看这是一个坏主意。在实际程序中将unique_lock作为类的成员也常常是一个坏主意。

+0

这有效,但为什么? – Kjir 2010-04-02 13:52:27

+0

那么boost :: unique_lock的用途是什么?它简单地使用互斥锁有什么优势?我猜想,RAII, – Kjir 2010-04-02 14:20:34

+2

。 http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization – 2010-04-02 14:22:48

3

锁定不应该是受保护资源的一部分,而应该是调用者的一部分,因为您有一个线程调用者。他们必须使用不同的unique_lock。

unique_lock的目的是用RAII锁定和释放互斥锁,所以你不必明确地调用unlock。

当unique_lock在方法体内声明时,它将属于调用线程堆栈。

所以更正确的使用方法是:

#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; 
} 

注意可变互斥的常量方法。

而boost mutex类型有一个scoped_lock typedef,它是很好的unique_lock类型。

4

顾名思义,互斥锁是recursive,但Lock不是。

这就是说,你在这里有一个设计问题。锁定操作会更好,不能从外部访问。

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获取关于mMutex
  • 它调用fetchFromCache这就要求set
  • set试图获取锁锁...

如果我们没有一个recursive_mutex,这将失败。

+0

问题是我有一个函数返回一个指针,我需要确保在我的类之外完成的操作的同步。所以这就是为什么我需要锁定和解锁方法,我把它们放在类里,以避免在外部代码中显式使用boost :: * _ lock。我应该想想更好的设计...... – Kjir 2010-04-08 16:30:51

+0

总是有一种解决方法,你可以将一个裁判计数所有者返回给锁来保证销毁。 – 2010-04-09 07:07:51

+0

“_锁定操作会更好,不能从外部访问_”如果您需要增加SynchronizedInt,该怎么办? – curiousguy 2011-10-22 19:27:03

相关问题