2012-02-21 17 views
2

私は複数のスレッドによって共有されるオブジェクトを持っています。オブジェクト全体をロックせずに個々のメンバ変数をロックしたいので、異なるスレッドが同時。 いくつかの記事を読んだあと、shared_mutexとgetter()/ setter()関数を使ってコードを記述します。C++ゲッター/セッター、ミューテックス、ファイングレインロック

class Test 
    { 
    public: 
    **// variable, shared_mutex and getter/setter for x** 
    double x; 
    boost::shared_mutex x_mutex; 
    double x_getter(); 
    void x_setter(); 
    **// variable, shared_mutex and getter/setter for y** 
    ...... 
    **// variable, shared_mutex and getter/setter for z** 
    ...... 
    }; 

    double Test::x_getter() 
    { 
     // get shared access 
     boost::shared_lock lock(_access); 
     return x; 
    } 

    void Test::x_setter() 
    { 
     // get exclusive access 
     boost::unique_lock lock(_access); 
     // do something with x; 
    } 

    //getter/setter functions for y and z. 
    ...... 

特に、メンバ変数の数が増えると、コードが不調に見えます。私はこのタイプの問題のためにそこに良い解決策があるのか​​どうか疑問に思っています。

ありがとうございました。

答えて

7

あなたは明らかにのみ、実際にデータを書き込む/読み込むの短い時間のためにロックを必要とするので、あなたはちょうどあなたが、その後メンバ変数として使う型に制御されたデータでそれをカプセル化することができます:

// note: you probably should add constructors as well 
template<typename T> struct synchronized 
{ 
public: 
    synchronized& operator=(T const& newval) 
    { 
    boost::unique_lock lock(mutex); 
    value = newval; 
    } 
    operator T() const 
    { 
    boost::unique_lock lock(mutex); 
    return value; 
    } 
private: 
    T value; 
    boost::shared_mutex mutex; 
}; 

class Test 
{ 
public: 
    synchronized<double> x; 
    synchronized<int> y; 
    synchronized<std::string> z; 
}; 

void foo(Test& t) 
{ 
    double read = t.x; // locked, via synchronized<double>::operator double() const 
    t.x = 3.14;  // locked, via synchronized<double>::operator= 
} 
+0

ありがとう、celtschk、私はあなたの方法を実装し、コードはもっときれいに見えます。 – 2607

1

あなたはこのメソッドがかなり不器用に見え、すぐに管理不能になるということは間違いありません。私は、データ依存を解くことによってマルチスレッドの問題をモデル化しようとしています。しかし、あなたが解決しようとしていることに対するさらなる文脈がなければ、私はその問題をモデル化する方法をアドバイスできません。

このようなアーキテクチャに既に投資していて、変更が遅すぎる場合は、これを検討してください。

template<class T> 
class SharedValiable 
{ 
    private: 
     T     myT; 
     boost::shared_mutex myTMutex; 

    public: 
     // 
     // Implement appropriate copy, assign and default 
     // to ensure proper value semantics 
     // 

     T getter() const 
     { 
      boost::shared_lock lock(_access); 
      return x; 
     } 

     void setter() 
     { 
      boost::unique_lock lock(_access); 
     } 
} 

これにより、各変数は当初意図したとおりに保護されますが、新しいメンバーの追加やクラスからのメンバーの削除が容易になります。さらに、テンプレートは、intなどのアトミックOS操作を使用できる特定のタイプに特化できます。例:

template<int> 
class SharedValiable 
{ 
    private: 
     T     myT; 

    public: 
     // 
     // Implement appropriate copy, assign and default 
     // to ensure proper value semantics 
     // 

     T getter() const 
     { 
      // no need to lock, updates are atomic 
      return x; 
     } 

     void setter() 
     { 
      // no mutex needed we will use an atomic OS op to update 
      InterlockedCompareAndExchange(myT, newVal); 
     } 
} 
0

一般に、私はこれに反対します。一般的にクラスを使ってやってみようとしているのは、メソッドに入り、メソッドから離れるときにオブジェクトの状態を保証することです。たとえば、リストオブジェクトでは、リンクが一貫性のある状態になる必要があります。カウントが正しいことが必要です。個々の変数をロックするだけでは、まれに起こることはありません。カウントの状態をロックしないでリンクを変更すると、更新の途中で別のスレッドによってクエリが実行されたときに、互いの間に横たわってしまいます。さらに悪いことに、同時に2つのリンクを変更して、ストレージに応じてもはや動作しなくなる厄介なリストを作成することができます。もちろん、その可能性のある例の1つですが、あなたはその考えを得るべきです。

複数のスレッドでオブジェクトの状態を表示できるようにするには、オブジェクト全体にリーダ/ライターロックを設定する必要があります。これにより、多くの読者が好きなだけ読めるようになりますが、オブジェクトの中間更新。

+0

私は一般的には同意しますが、スレッドセーフではないパブリックデータを持つ理由があります。これはコメントに適しています。 –

+0

上部の告発については、正しいかもしれません。答えの真の答えは、リーダー/ライターのロックを使用することです。 – Joel

関連する問題