17

私はレガシーコードを調べていて、次のスニペットを見つけました:

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 の場合は、機能する可能性があります。しかし、順序が逆になるとどうなるでしょうか?

それは設計上の問題ですか?

4

9 に答える 9

37

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

于 2009-03-14T18:32:20.027 に答える
5

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

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

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

于 2009-03-14T18:33:25.077 に答える
4

グローバル変数にアクセスしている場合は、スレッド セーフが必要になる可能性があります。はい

例えば。私の「ウィンドウ」クラスは、コンストラクターのリスト「knownWindows」に自分自身を追加し、デストラクターで自分自身を削除します。「knownWindows」はスレッドセーフである必要があるため、両方が実行中にミューテックスをロックします。

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

于 2009-03-14T18:46:12.097 に答える
4

スレッドが 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()を実行しているスレッドが終了する前にオブジェクトが破棄されないようにする必要があります。

于 2009-03-15T00:08:06.940 に答える
3

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

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

于 2009-03-14T18:45:14.020 に答える
0

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

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

于 2009-03-14T19:17:27.157 に答える
0

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

于 2009-03-14T18:45:23.487 に答える
0

あなたのコメントには、「ここではグローバルメンバーはアクセスされていません」と書かれているので、そうではないと思います. オブジェクトを作成したスレッドのみがオブジェクトを破棄する必要があるため、他のどのスレッドからオブジェクトを保護しますか?

私自身は、1 つのオブジェクトだけが別のサブオブジェクトを所有し、そのサブオブジェクトへの参照を持つ他のすべてのオブジェクトがツリーのさらに下の子孫である、規則正しい作成と破棄が好きです。これらのサブオブジェクトのいずれかが異なるスレッドを表している場合、破壊がツリーを上っていく前にそれらは確実に完了します。

例:

  • main() オブジェクト A を作成
    • オブジェクト A がオブジェクト B を含む
      • オブジェクト B がオブジェクト C を含む
        • オブジェクト C は、オブジェクト A と B にアクセスするスレッドを作成します
        • オブジェクト C のデストラクタが実行され、そのスレッドが終了するのを待ちます
      • オブジェクト B のデストラクタが実行されます
    • オブジェクト A のデストラクタが実行されます
  • main() が返す

オブジェクト A と B のデストラクタは、スレッドについてまったく考える必要がなく、オブジェクト C のデストラクタは、それ自体を作成するために選択したスレッドとの通信メカニズム (たとえば、イベントを待機する) を実装するだけで済みます。

オブジェクトへの参照 (ポインタ) を任意のスレッドに渡し始めた場合にのみ問題が発生する可能性がありますが、これらのスレッドがいつ作成され、いつ破棄されるかを追跡することはありませんが、それを行う場合は、参照カウントを使用する必要があります。その場合、デストラクタが呼び出されるまでには手遅れです。オブジェクトへの参照がまだある場合は、そのデストラクタを呼び出そうとする人さえいないはずです。

于 2010-01-28T23:16:14.167 に答える