2009-03-14 3 views
15

私は、レガシーコードを通過し、以下のスニペットが見つかりました:それはデストラクタを守るために万が一のに役立ちますデストラクタはスレッドセーフであるべきですか?

MyClass::~MyClass() 
{ 
    EnterCriticalSection(&cs); 

//Access Data Members, **NO Global** members are being accessed here 


    LeaveCriticalSection(&cs); 
} 

を私は疑問に思って?

は、シナリオを検討してください。

1. Thread1 - About to execute any of the member function which uses critical section 
2. Thread2- About to execute destructor. 

実行の順序は、1 => 2、それがうまくいくかもしれないの場合。しかし、もし注文が逆転すればどうなるでしょうか?

デザイン上の問題ですか?

答えて

34

オブジェクトが使用されているときにデストラクタを呼び出すと、は呼び出されません。このような状況に対処する場合は、には基本的な修正が必要です。ただし、デストラクタは他のもの(破壊されるクラスとは無関係)を変更したい場合があり、クリティカルセクションが必要な場合があります(グローバルカウンタを減算するなど)。

+0

スレッドセーフである必要がありますか?クラスが「2度破壊される」可能性はありますか? –

+0

@TomášZato:いいえ。(些細ではないデストラクタを持つ)クラスが「2回破壊された」場合、スレッドセーフな方法で発生するかどうかは問われません。いずれにしてもUBです。 – MikeMB

+1

答えの年齢にもかかわらず、私は同意しません。メンバ変数が、dtorが呼び出されるスレッド以外のスレッドによって変更されている場合は、同期を適用する必要があります。しかし、それは「クリティカルセクション」である必要はありません。必要なのは、2つのスレッド間に「同期」関係があるということです。スレッドが同じでない限り、どちらのスレッドでも適切なメモリバリアが必要になることがあります。 – CouchDeveloper

3

あなたはもっと根本的な問題があると思います。別のスレッドが依然としてメンバ関数を呼び出している間に、あるスレッドでオブジェクトを破棄するのは合法ではありません。これ自体は間違っています。

デストラクタをクリティカルセクションで正常に保護しても、他のスレッドが残りの関数の実行を開始するとどうなりますか?これは、削除されたオブジェクトに対して行うことになります(割り当て場所によっては、ガベージメモリまたは単純な無効オブジェクト)。

オブジェクトがまだ使用中に破棄されないように、コードを変更する必要があります。

3

「スレッドセーフ」を定義します。これらはおそらく現代コンピューティングの最も理解できない言葉の2つです。

しかし、2つの異なるスレッドからデストラクタが2回入力される可能性がある場合(symchronisationオブジェクトの使用が示唆するように)、コードは深刻なdoo-dooにあります。求めているオブジェクトを削除しているオブジェクトは、これを管理している必要があります。同期が行われているはずのレベル(おそらく)です。

0

違いはありません。あなたが言うように、呼び出しの順序が逆転した場合、破壊されたオブジェクトのメンバ関数を呼び出すと失敗することになります。同期はその論理エラーを修正できません(まず、メンバー関数呼び出しは、破壊されたロックオブジェクトを取得しようとしています)。

4

グローバル変数にアクセスする場合は、スレッドの安全性が必要な場合があります。

たとえば、私の "Window"クラスは、コンストラクタの "knownWindows"リストに自身を追加し、デストラクタで自身を削除します。 "knownWindows"はスレッドセーフである必要があるため、両方ともmutexをロックします。

一方、デストラクタが破壊されるオブジェクトのメンバにしかアクセスしない場合は、設計上の問題があります。

+0

それは正しい答えです。 –

0

Neil ButterWorthからのコメントが2番目です。絶対に、myclassの削除とアクセスを担当するエンティティは、これをチェックする必要があります。

この同期は、MyClassタイプのオブジェクトが作成された瞬間から実際に開始されます。

2

スレッドがACE_Task_Baseオブジェクトで実行され、オブジェクトが別のスレッドから破棄されるACEスレッドの場合があります。デストラクタはロックを取得し、条件を待つ直前に含まれているスレッドに通知します。 ACE_Task_Base信号上で実行されているスレッドは、出口での状態を通知し、デストラクタ完全で最初のスレッド終了することができます:このコードで

class PeriodicThread : public ACE_Task_Base 
{ 
public: 
    PeriodicThread() : exit_(false), mutex_() 
    { 
    } 
    ~PeriodicThread() 
    { 
     mutex_.acquire(); 
     exit_ = true; 
     mutex_.release(); 
     wait(); // wait for running thread to exit 
    } 
    int svc() 
    { 
     mutex_.acquire(); 
     while (!exit_) { 
     mutex_.release(); 
     // perform periodic operation 
     mutex_.acquire(); 
     } 
     mutex_.release(); 
    } 
private: 
    bool exit_; 
    ACE_Thread_Mutex mutex_; 
}; 

を、デストラクタは、オブジェクトがないことを保証するために、スレッドの安全性の技術を使用する必要があります。実行中のスレッドの前に破棄されましたsvc()終了します。

0

あなたのコメントには「いいえ、グローバルメンバーはここにアクセスしていません」と私は推測していません。オブジェクトを作成したスレッドだけがそれを破棄する必要があります。他のスレッドから保護するでしょうか?

私は、単一のオブジェクトだけが別のサブオブジェクトを所有しており、そのサブオブジェクトへの参照を持つ他のオブジェクトはツリーの下位の子孫です。それらのサブオブジェクトのいずれかが異なるスレッドを表す場合、破壊がツリーの上に進む前に必ず完了するようにします。

例:

  • ()
    • オブジェクトAがオブジェクトB
      • オブジェクトBがオブジェクトC
        • オブジェクトCがアクセスするスレッドを作成含ま含むオブジェクトを作成しますオブジェクトAおよびB
        • Bのデストラクタは、
      • オブジェクトAのデストラクタは、
      (主
    • を実行する実行
  • オブジェクトを終了するそのスレッドを待っているオブジェクトCのデストラクタの実行)のため

デストラクタを返しますオブジェクトAとBはスレッドをまったく考える必要はなく、オブジェクトCのデストラクタは何らかの通信メカニズム(たとえばイベント待ち)を実装するだけでよく、自分自身を作成するために選択したスレッドを使用します。

オブジェクトへの参照(ポインタ)を任意のスレッドに渡し始めると問題が発生するのは、それらのスレッドがいつ作成され破棄されたかを把握せずに済みますが、その場合は参照カウントダウンし、あなたがいればデストラクタが呼ばれるまでには遅すぎます。まだオブジェクトへの参照がある場合、誰もそのデストラクタを呼び出そうとするべきではありません。

関連する問題