我有一个使用boost互斥锁和这样的锁定的自定义类(只有相关部分):
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;
}
这是输出:
sb@dex $ ./src/test
Locking
Locked
Locking
terminate called after throwing an instance of 'boost::lock_error'
what(): boost::lock_error
zsh: abort ./src/test
为什么会这样?我错误地理解了一些概念吗?
答案 0 :(得分: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
的要点是允许在给定线程中进行链锁定,如果你有复杂的操作在某些情况下相互调用,可能会发生这种情况。
例如,让我们添加tweak get:
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
,则会失败。
答案 1 :(得分: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;
}
注意const方法的互斥锁上的mutable。
boost mutex类型有一个scoped_lock typedef,这是一个很好的unique_lock类型。
答案 2 :(得分: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实例,例如foe示例_lock
和_lock_2
;。
<强>更新强>
我想补充一点,您的课程有公共方法lock()
和unlock()
,从我的观点来看,这是一个不错的主意。在实际程序中将unique_lock作为类的成员也必须经常是一个坏主意。