3

必要なクリーンアップを実行するデストラクタがあります (プロセスを強制終了します)。SIGINT がプログラムに送信された場合でも実行する必要があります。私のコードは現在次のようになっています:

typedef boost::shared_ptr<PidManager> PidManagerPtr
void PidManager::handler(int sig)
{
  std::cout << "Caught SIGINT\n";
  instance_.~PidManagerPtr();  //PidManager is a singleton
  exit(1);
}
//handler registered in the PidManager constructor

これは機能しますが、デストラクタを明示的に呼び出すことに対して多くの警告があるようです。これはこの状況で正しいことですか、それとも「より正しい」方法はありますか?

4

6 に答える 6

5

そのオブジェクトがシングルトンの場合、共有ポインターを使用する必要はありません。(ひとつだけ!)

に切り替えると、auto_ptr呼び出すことができますrelease()。または、おそらくscoped_ptr、呼び出しreset()ます。

exit()これはすべて、静的に構築されたオブジェクトを破壊することを 99% 確信しています。(どのシングルトンになる傾向があります。)私が知っているexit()のは、登録されたatexit()関数を呼び出すことです。

シングルトンが exit によって自動的に破棄されない場合、適切な方法はatexitフックを作成することです。

void release_singleton(void)
{
    //instance_.release();
    instance_.reset();
}

// in main, probably
atexit(release_singleton);
于 2009-11-13T21:35:14.493 に答える
2

object が配置 new で構築されていない限り、明示的にデストラクタを呼び出さないでください。クリーンアップ コードを別の関数に移動し、代わりに呼び出します。同じ関数がデストラクタから呼び出されます。

于 2009-11-13T21:23:02.240 に答える
2

これを行うことは非常に悪い考えであることが判明しました。起こっている奇妙なことの量は途方もないです。

何が起こっていた

shared_ptr には、ハンドラーに入る 2 の use_count がありました。1 つの参照は PidManager 自体にあり、もう 1 つは PidManager のクライアントにありました。shared_ptr (~PidManager() ) のデストラクタを呼び出すと、use_count が 1 減ります。次に、GMan が示唆したように、exit() が呼び出されたときに、静的に初期化された PidManagerPtr instance_ のデストラクタが呼び出され、use_count が 0 に減り、PidManager デストラクタが呼び出されました。明らかに、PidManager に複数のクライアントがあった場合、use_count は 0 にならず、まったく機能しませんでした。

これにより、instance_.reset() の呼び出しが機能しなかった理由についてのヒントも得られます。この呼び出しは実際に参照カウントを 1 減らします。しかし、残りの参照は PidManager のクライアントの shared_ptr です。その shared_ptr は自動変数であるため、そのデストラクタは exit() で呼び出されません。instance_ デストラクタが呼び出されますが、reset() であるため、もはや PidManager インスタンスを指していません。

ソリューション

私は shared_ptrs の使用を完全に放棄し、代わりに Meyers Singleton を使用することにしました。今私のコードは次のようになります:

void handler(int sig)
{
     exit(1);
}

typedef PidManager * PidManagerPtr
PidManagerPtr PidManager::instance()
{
    static PidManager instance_;
    static bool handler_registered = false;
    if(!handler_registered)
    {
        signal(SIGINT,handler);
        handler_registered = true;
    }
    return &instance_;
 }

exit を明示的に呼び出すと、静的に初期化された PidManager instance_ のデストラクタを実行できるため、他のクリーンアップ コードをハンドラに配置する必要はありません。これにより、PidManager が一貫性のない状態にあるときにハンドラーが呼び出されるという問題が適切に回避されます。

于 2009-11-16T16:42:07.203 に答える
1

シグナルハンドラーでは、実際には何もしたくありません。最も安全な方法は、フラグ(グローバル揮発性ブールなど)を設定し、プログラムの定期的なイベントループでそのフラグを頻繁にチェックし、それがtrueになっている場合は、そこからクリーンアップ/シャットダウンルーチンを呼び出すことです。

シグナルハンドラーはアプリケーションの他の部分と非同期で実行されるため、シグナルハンドラーの内部からそれ以上のことを行うのは安全ではありません。操作するデータが一貫していない状態になる可能性があります。(そして、シグナルハンドラーからミューテックスや他の同期を使用することは許可されていません-シグナルはそのようにかなり邪悪です)

ただし、ブール値を常にポーリングする必要があるという考えが気に入らない場合は、シグナルハンドラー内から(少なくともほとんどのOSで)実行できるもう1つのことは、ソケットでバイトを送信することです。したがって、事前にsocketpair()を設定し、ソケットペアのもう一方の端で通常のイベントループselect()(またはその他)を使用することができます。そのソケットでバイトを受信すると、シグナルハンドラーがそのバイトを送信したに違いないことを認識しているため、クリーンアップするときが来ました。

于 2009-11-14T07:02:44.843 に答える
0

shared_ptrでreset()を呼び出すだけで、インスタンスが削除されます。

于 2009-11-13T21:29:26.703 に答える
0

もう 1 つの方法は、(最初の使用時またはメインで) シングルトンを動的に割り当て、deleteそれをクリーンアップすることです。

ええ。あなたの PidManagerPtr は実際には動的に割り当てられたオブジェクトを指していると思います...しかし、boost::shared_ptr は実際に再割り当て時にクリーンアップしませんか? したがって、次のことを行うだけで十分です。

instance_ = 0;

?

于 2009-11-13T21:22:22.963 に答える