1

次のようなブースト ミューテックスとロックを使用するカスタム クラスがあります (関連する部分のみ)。

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

なぜこうなった?いくつかの概念を間違って理解していますか?

4

3 に答える 3

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、これは失敗します。

于 2010-04-02T17:16:29.000 に答える
3

スレッドごとに呼び出し元が 1 つあるため、ロックは保護されたリソースの一部ではなく、呼び出し元の一部である必要があります。それらは異なる 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 メソッドのミューテックスのミュータブルに注意してください。

また、ブースト ミューテックス タイプには、優れた unique_lock タイプである scoped_lock typedef があります。

于 2010-04-02T14:50:51.867 に答える
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の同じインスタンスを_lock2回使用しますが、これは問題です。再帰的ミューテックスのメソッドlock()とunock()を直接使用するか、敵の例_lock_lock_2;のようなunique_lockの2つの異なるインスタンスを使用する必要があります。

アップデート

あなたのクラスにはパブリックメソッドがlock()ありunlock()、実際のプログラムでの私の観点からは、それは悪い考えです。また、実際のプログラムのクラスのメンバーとしてunique_lockを使用することは、多くの場合、悪い考えです。

于 2010-04-02T13:49:07.423 に答える