代码之家  ›  专栏  ›  技术社区  ›  Kjir

为什么boost::递归mutex不能按预期工作?

  •  1
  • Kjir  · 技术社区  · 15 年前

    我有一个自定义类,它使用boost 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();
    }
    

    如果我试图锁定对象不止一次 从同一条线 ,我得到一个异常(锁定错误):

    #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
    

    为什么会这样?我对某些概念理解不正确吗?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Sergey Kurenkov    15 年前

    试试这个:

    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();
    }
    

    您使用唯一锁的同一个实例 _lock 两次,这是个问题。 您要么直接使用递归互斥体的方法lock()和unock(),要么使用两个不同的惟一锁实例,如foe示例 _锁定 _lock_2 ;

    更新

    我想补充一点,你的类有公共方法 lock() unlock() 在我看来,在一个真正的程序中,这是一个坏主意。在一个真正的程序中作为类的成员拥有唯一的锁通常是一个坏主意。

        2
  •  4
  •   Matthieu M.    15 年前

    顾名思义,互斥体是 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
    • 设置 试图获取锁…

    如果我们没有 递归互斥 ,这将失败。

        3
  •  3
  •   fa.    15 年前

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

    唯一锁的目的是用raii锁定和释放互斥体,因此您不必显式调用unlock。

    当在方法体中声明唯一锁时,它将属于调用线程堆栈。

    所以更正确的用法是:

    #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方法的互斥体上的变量。

    Boost互斥类型有一个作用域锁typedef,这是一个很好的唯一锁类型。