3

複数のプロジェクト間で共有されるクラスがあり、その一部はシングルスレッドで使用され、一部はマルチスレッドで使用されます。シングルスレッド ユーザーはミューテックス ロックのオーバーヘッドを望んでおらず、マルチスレッド ユーザーは独自のロックを実行したくなく、オプションで「シングルスレッド モード」で実行できるようにしたいと考えています。したがって、実行時に実際のミューテックスと「ダミー」ミューテックスを選択できるようにしたいと考えています。

理想的にはshared_ptr<something>、実際のミューテックス オブジェクトまたは偽のミューテックス オブジェクトを割り当てます。次に、その内容に関係なく、これを「ロック」します。

unique_lock<something> guard(*mutex);
... critical section ...

現在は がありますがsignals2::dummy_mutex、 と共通の基本クラスを共有していませんboost::mutex

では、上記の例よりもロック/ガード コードを複雑にすることなく、実際のミューテックスとダミー ミューテックス (signals2 のいずれかまたは他のもの) を選択するエレガントな方法は何ですか?

そして、代替案を指摘する前に:

  • コンパイル時に実装を選択することもできましたが、プリプロセッサ マクロは見苦しく、プロジェクトの構成を維持するのは大変です。
  • マルチスレッド環境でのクラスのユーザーは、クラスが内部で独自のロックを行うよりも、クラスの使用をロックする責任を負いたくないでしょう。
  • 「スレッドセーフなラッパー」が実用的なソリューションになるには、あまりにも多くの API と既存の使用法が関係しています。
4

8 に答える 8

5

このようなものはどうですか?テストされていませんが、ほぼ OK です。ミューテックスが適切な種類の構造をサポートしている場合は、テンプレート クラスにポインターではなく値を保持させることを検討してください。それ以外の場合は、値の動作を取得するために MyMutex クラスを特殊化できます。

また、コピーや破壊に注意を払っていません..それは読者の演習として残します;)(ポインタではなくshared_ptrまたは値を保存することでこれを修正する必要があります)

ああ、コードは明示的なロック/ロック解除よりも RAII を使用した方が良いでしょう...しかし、それは別の質問です。あなたのコードの unique_lock が何をしていると思いますか?

struct IMutex
{
  virtual ~IMutex(){}
  virtual void lock()=0;
  virtual bool try_lock()=0;
  virtual void unlock()=0;
};

template<typename T>
class MyMutex : public IMutex
{
  public:
    MyMutex(T t) : t_(t) {}
    void lock() { t_->lock(); }
    bool try_lock() { return t_->try_lock(); }
    void unlock() { t_->unlock(); }
  protected:
    T* t_;
};

IMutex * createMutex()
{
  if( isMultithreaded() )
  {
     return new MyMutex<boost::mutex>( new boost::mutex );
  }
  else
  {
     return new MyMutex<signal2::dummy_mutex>( new signal2::dummy_mutex );
  }
}


int main()
{
   IMutex * mutex = createMutex();
   ...
   {
     unique_lock<IMutex> guard( *mutex );
     ...
   }

}
于 2010-02-02T03:46:48.897 に答える
3

2つのミューテックスクラスは共通の基本クラスを共有しsignals2::dummy_mutexboost::mutexいないため、「外部ポリモーフィズム」のようなものを使用して、それらをポリモーフィックに処理できるようにすることができます。次に、それらを一般的なミューテックス/ロックインターフェイスのロック戦略として使用します。ifこれにより、ロックの実装で""ステートメントを使用することを回避できます。

:これは基本的に、Michaelが提案したソリューションが実装するものです。私は彼の答えで行くことをお勧めします。

于 2010-02-02T04:51:10.170 に答える
1

について聞いたことがありPolicy-based Designますか?

インターフェイスを定義することができLock Policy、ユーザーは希望するポリシーを選択できます。使いやすさのために、「デフォルト」ポリシーはコンパイル時の変数を使用して正確に指定されます。

#ifndef PROJECT_DEFAULT_LOCK_POLICY
#define PROJECT_DEFAULT_LOCK_POLICY TrueLock
#endif

template <class LP = PROJECT_DEFAULT_LOCK_POLICY>
class MyClass {};

このようにして、ユーザーは簡単なコンパイル時の切り替えでポリシーを選択でき、一度に 1 つのインスタンスでオーバーライドできます ;)

于 2010-02-02T08:38:21.937 に答える
0

参考までに、これが私が最終的に作成した実装です。

私は抽象基本クラスを廃止し、それをno-op「ダミー」実装とマージしました。shared_ptrまた、暗黙の変換演算子を使用した派生クラスにも注意してください。少しトリッキーすぎると思いますが、shared_ptr<IMutex>以前は変更のないオブジェクトを使用していたboost::mutexオブジェクトを使用できます。

ヘッダーファイル:

class Foo {
   ...
private:
    struct IMutex {
        virtual ~IMutex()       { }
        virtual void lock()     { }
        virtual bool try_lock() { return true; }
        virtual void unlock()   { }
    };
    template <typename T> struct MutexProxy;

    struct MutexPtr : public boost::shared_ptr<IMutex> {
        operator IMutex&() { return **this; }
    };

    typedef boost::unique_lock<IMutex> MutexGuard;

    mutable MutexPtr mutex;
};

実装ファイル:

template <typename T>
struct Foo::MutexProxy : public IMutex {
    virtual void lock()     { mutex.lock(); }
    virtual bool try_lock() { return mutex.try_lock(); }
    virtual void unlock()   { mutex.unlock(); }
private:
    T mutex;
};

Foo::Foo(...) {
    mutex.reset(single_thread ? new IMutex : new MutexProxy<boost::mutex>);
}

Foo::Method() {
    MutexGuard guard(mutex);
}
于 2010-02-02T20:27:08.560 に答える
0

一般に、ミューテックスは、リソースが複数のプロセス間で共有されている場合にのみ必要です。オブジェクトのインスタンスが (マルチスレッドの可能性がある) プロセスに対して一意である場合、多くの場合、クリティカル セクションの方が適切です。

Windows では、クリティカル セクションのシングル スレッド実装はダミーです。使用しているプラ​​ットフォームがわからない。

于 2010-02-02T07:43:28.917 に答える
0

これで十分ではありませんか?

   class SomeClass
    {
    public:
        SomeClass(void);
        ~SomeClass(void);
        void Work(bool isMultiThreaded = false)
        {
            if(isMultiThreaded)
           {
               lock // mutex lock ...
               {
                    DoSomething
               }
           }
           else
           {
                DoSomething();
           }
       }   
    };
于 2010-02-02T02:59:56.790 に答える