2012-07-25 1 views
6

私はC++のマルチスレッドの基本については、少なくとも上手く取り組んでいると思いますが、共有リソースの周りにミューテックスをロックするという明確な答えは得られませんでしたコンストラクタまたはデストラクタで宣言します。私はあなたが両方の場所でロックしなければならないという印象を受けていましたが、最近の同僚は反対しています。以下のクラスは、複数のスレッドによってアクセスされたふり:もちろん、すべてのコンストラクタとデストラクタの共有リソースをロックする

class TestClass 
{ 
public: 

    TestClass(const float input) : 
     mMutex(), 
     mValueOne(1), 
     mValueTwo("Text") 
    { 
     //**Does the mutex need to be locked here? 
     mValueTwo.Set(input); 
     mValueOne = mValueTwo.Get(); 
    } 

    ~TestClass() 
    { 
    //Lock Here? 
    } 

    int GetValueOne() const 
    { 
     Lock(mMutex); 
     return mValueOne; 
    } 

    void SetValueOne(const int value) 
    { 
     Lock(mMutex); 
     mValueOne = value; 
    } 

    CustomType GetValueTwo() const 
    { 
     Lock(mMutex); 
     return mValueOne; 
    } 

    void SetValueTwo(const CustomType type) 
    { 
     Lock(mMutex); 
     mValueTwo = type; 
    } 

private: 

    Mutex mMutex; 
    int mValueOne; 
    CustomType mValueTwo; 
}; 

を初期化リストを通じて安全であるが、何コンストラクタ内のステートメントについてのでしょうか?デストラクタでは、非スコープのロックを行い、ロックを解除することは有益でしょう(基本的にはpthread_mutex_destroyを呼び出します)。

+3

あなたは「クラス」であると言います複数のスレッド間で使用されている場合は、TestClass型のオブジェクトが複数のスレッドで使用されている可能性があります。その場合でも、単一のオブジェクトを作成しているだけなので、コンストラクタでロックする必要はありません。両方のスレッドが同時にコンストラクタ内にある場合、2つの別々のオブジェクトを作成しています。オブジェクトの構築が完了する前に(たとえば)mValueTwoが使用されていないことを確認するために、オブジェクトの構築をロックする方が理にかなっています。デストラクタは、データが破壊されている間にアクセスされないように、ロックする必要があるようです。 – Rollie

+0

@Rollieはい、私はオブジェクトが共有されることを意味しました。だから、私が作成する場合: – Brett

+1

@Rollie:破壊中のクラスへのアクセスは、インスタンスのライフタイム管理のバグです。プログラムはすでに破損しているときに破損しています(破棄中にアクセスできる場合、 )。 –

答えて

12

複数のスレッドが同じオブジェクトを構築することはできません。オブジェクトを完全に構築する前に、そのスレッドを使用することもできません。したがって、正当なコードでは、ロックなしの構築は安全です。

破壊は少し難しい場合があります。しかし、やはり、オブジェクトの適切なライフタイム管理によって、スレッドがまだそれを使用している可能性がある場合にオブジェクトが破壊されることはありません。

共有ポインタは、例えばこれを達成するのに役立ちます。 :

  • 場合、すべてのスレッド(必要であれば、それを構築したスレッドを含む)オブジェクトへのアクセスを必要とするすべてのスレッドへ
  • パス共有ポインタ
  • オブジェクトが破壊される特定のスレッドでオブジェクトを構築共有ポインタを解放しました

もちろん、他の有効なアプローチが存在します。重要な点は、オブジェクトの存続期間の3つの主要段階(建設、使用、破壊)の境界を適切に保つことです。これらの段階の間に重複を許さないでください。

+1

shared_ptrの使用は一般的には不十分です。これは、shared_ptrを削除する最後のスレッドはデストラクタを実行しますが、そのスレッドはオブジェクトを変更した最後のスレッドではない可能性があります。オブジェクトにベクトルやマップなどの複雑なデータメンバが含まれていて、デストラクタがロックを取得および解放しない場合、デストラクタは古いメモリを参照してクラッシュする可能性があります。 –

+0

@MichiHenning:スレッドは、オブジェクト上のすべての操作を完了した後に共有ポインタ*を解放しなければなりません。これは非常に妥当な(明らかではないが)必要条件です。したがって、ロックは不要です。古いメモリは確かに理論上可能ですが、それに対処するには、ロックしないでメモリバリアが必要です。 (与えられたいくつかの一般的なスレッドライブラリには、特定のロック操作のバリアが含まれているので、多分それがあなたが意味するものです) –

+0

ここにシナリオがあります。スレッドAにshared_ptr を作成し、スレッドBにshared_ptr(正しくインターロックする)を渡します。スレッドAはfooの状態を更新し、そのポインタを破棄します。その後、スレッドBはshared_ptrを落とし、スレッドBはfooのデストラクタを呼び出します。スレッドAが最後に更新されたfooのためにスレッドBがメモリバリアを越えていない場合、fooのデストラクタは古いデータで動作します。 fooにマップなどの複雑なデータメンバーが含まれていると、クラッシュする可能性があります。あなたは、記憶の壁が必要であることは間違いありません。ミューテックスもまた障壁を作り出します。 –

1

コンストラクタ内でロックを解除する必要はありません。外部の誰かがその時点でそのデータにアクセスできる唯一の方法は、コンストラクタ自体から渡す場合です(または呼び出しなどの未定義の動作仮想メソッド)。

は、[編集:デストラクタ程度除去された部分、あなたが死んだかもしれないオブジェクトからリソースにアクセスしようとしている場合、コメントは当然主張するよう以来、あなたは大きな問題を抱えている]

+1

破壊されている間に別のスレッドがオブジェクトにアクセスする可能性がある場合は、バグがあり、ロックが役立たない(破棄するスレッドが最初にミューテックスを取得するとどうなるか考えてください)。また、私は 'shared_ptr'でミューテックスをどのようにラップするのか分かりません。 – interjay

+0

あなたが正しいですが、死にそうなオブジェクトからデータにアクセスしている可能性がある場合は、問題があることに同意します。 shared_ptrの提案は、オブジェクトと一緒にmutexが破壊される状況を避けることでしたが、そのオブジェクトの中にあるものにアクセスする前にそのmutexをロックしなければならないと考えています。私は私の答えを編集します。 –

関連する問題