web-dev-qa-db-ja.com

`std :: exit`が期待どおりにデストラクタをトリガーしないのはなぜですか?

#include <cstdlib>
#include <thread>
#include <chrono>
#include <iostream>

using namespace std;
using namespace std::literals;

struct A
{
    int n_ = 0;
    A(int n) : n_(n) { cout << "A:" << n_ << endl; }
    ~A() { cout << "~A:" << n_ << endl; }
};

A a1(1);

int main()
{
    std::thread([]()
    {
        static A a2(2);
        thread_local A a3(3);
        std::this_thread::sleep_for(24h);
    }).detach();

    static A a4(4);
    thread_local A a5(5);

    std::this_thread::sleep_for(1s);
    std::exit(0);
}

私のコンパイラはclang 5.0-std=c++1zです。

出力は次のとおりです。

A:1
A:2
A:4
A:5
A:3
~A:5
~A:2
~A:4
~A:1

~A:3がないことに注意してください。これは、オブジェクトA a3が破棄されなかったことを意味します。

ただし、 cppref によると:

std::exitにより、通常のプログラム終了が発生します。いくつかのクリーンアップ手順が実行されます。

スレッドローカルストレージ期間...を持つオブジェクトのデストラクタは、呼び出されることが保証されています。

24
xmllmx

スレッドの保存期間を持つオブジェクトは、exitを呼び出すスレッドに対してのみ破棄されることが保証されています。 C++ 14(N4140)、[support.start.term] 18.5/8(私の強調)を引用:

_[[noreturn]] void exit(int status)
_

関数exit()には、この国際規格で追加の動作があります。

  • まず、スレッドの保存期間がそして現在のスレッドに関連付けられているのオブジェクトが破棄されます。次に、静的ストレージ期間を持つオブジェクトが破棄され、atexitを呼び出して登録された関数が呼び出されます。破棄と呼び出しの順序については、3.6.3を参照してください。 (exit()を呼び出した結果として自動オブジェクトが破棄されることはありません。)関数がスローされた例外のハンドラーを提供しないため、コントロールがexitによって呼び出された登録済み関数を離れる場合、std::terminate()は(15.5.1)と呼ばれます。
  • 次に、(_<cstdio>_で宣言された関数シグネチャによって仲介される)開いているすべてのCストリームがフラッシュされ、開いているすべてのCストリームが閉じられ、tmpfile()を呼び出して作成されたすべてのファイルが削除されます。 。
  • 最後に、制御はホスト環境に戻されます。ステータスがゼロまたは_EXIT_SUCCESS_の場合、実装で定義された形式のステータス正常終了が返されます。ステータスが_EXIT_FAILURE_の場合、実装で定義された形式のステータスの失敗した終了が返されます。それ以外の場合、返されるステータスは実装定義です。

したがって、この標準は、exitを呼び出すスレッド以外のスレッドに関連付けられたスレッドストレージ期間を持つオブジェクトの破棄を保証しません。

ここでの問題は、プロセスを終了すると、スレッドが(最新のマルチタスクオペレーティングシステムでは)強制的に強制終了されることです。このスレッドの強制終了はOSレベルで発生し、OSはオブジェクトやデストラクタについて何も知りません。

15