私が std::thread
を起動し、それを detach()
したとすると、一旦それを表現した std::thread
がスコープ外に出ても、スレッドは実行し続けます。
さらに、プログラムが切り離されたスレッドに参加するための信頼できるプロトコルを持っていないと仮定します1 そのため、切り離されたスレッドは main()
が終了しても実行されたままになっています。
標準規格(正確には N3797 C++14 ドラフト)には、1.10 にも 30.3 にも適切な文言が含まれておらず、このような事態を記述したものは見当たりません。
なぜなら、参加するためにどのようなプロトコルを発明するにしても、シグナル伝達はスレッドがまだ動作している間に行われなければならず、OSスケジューラはシグナル伝達が行われた直後にスレッドを1時間スリープさせることを決定し、受信側はスレッドが実際に終了したことを確実に検出することができないからです。
もし、デタッチされたスレッドが実行されている状態で main()
が終了することが未定義の動作であるなら、メインスレッドが終了しない限り std::thread::detach()
の any 使用は未定義の動作です 2.
したがって、デタッチされたスレッドが実行されている状態で main()
が終了すると、_定義された_効果があるはずです。問題は、その効果がどこで(C++の標準でも、POSIXでも、OSのドキュメントでもなく)定義されているかということです。
2 切り離されたスレッドは(std::thread::join()
の意味で)参加することができません。切り離されたスレッドからの結果を待つことはできますが (例えば std::packaged_task
からの future やカウントセマフォ、フラグや条件変数によって)、そのスレッドが実行を終了したことを保証するものではありません (訳注:このスレッドは `std::thread::join()' という意味で使われています)。実際、スレッドの最初の自動オブジェクトのデストラクタにシグナリングの部分を入れない限り、一般的に、シグナリングのコードの後に実行されるコード(デストラクタ)が存在します。もしOSがメインスレッドが結果を消費し、デタッチドスレッドが前記デストラクタを実行し終わる前に終了するようにスケジュールした場合、何が起こるでしょうか^Wisは定義されているのでしょうか?
元の質問 "main()
が終了したとき、切り離されたスレッドはどうなるのか" に対する答えは、次のとおりです。
他のスレッドの(自動|thread_local)変数や静的オブジェクトに触れない限り、(標準では停止するとは書かれていないので)実行を継続し、それはうまく定義されています。
これは、スレッドマネージャを静的オブジェクトとして許可しているように見えます([basic.start.term]/4のノートには、そのように書かれています、@dypさんのポインタに感謝します)。
静的オブジェクトの破壊が終了すると、シグナルハンドラで許可されたコードのみが実行できる体制に入るため、問題が発生する([basic.start.term]/1, 1st sentence)。C++の標準ライブラリのうち、<atomic>
ライブラリのみです([support.runtime]/9、2文目)。特に、condition_variable
は一般的に除外されています ('atomic>
の一部ではないので、シグナルハンドラで使用するために保存するかどうかは実装で決まります)。
この時点でスタックを解放していない限り、未定義の振る舞いを回避する方法はわかりません。
2つ目の質問 "分離したスレッドを再び結合することは可能か" に対する答えは、次のとおりです。
これは *_at_thread_exit
関数群 (notify_all_at_thread_exit()
, std::promise::set_value_at_thread_exit()
, ...) によって行われます。
質問の脚注 [2] にあるように、条件変数やセマフォ、アトミックカウンタにシグナルを送ることは、切り離されたスレッドに参加するには十分ではありません (待機中のスレッドがそのシグナルを受け取る前に、その実行終了が 起こった ことを保証する意味で)。一般に、例えば条件変数の notify_all()
の後に実行するコードは多くなり、特に自動オブジェクトやスレッドローカルのデストラクタが多くなりますから。
スレッドが最後に行うこととしてシグナルを実行すること (自動オブジェクトやスレッドローカルオブジェクトのデストラクタが 発生した 後*) は、 _at_thread_exit
関数ファミリーがそのために設計されたものです。
ですから、標準が要求する以上の実装保証がない場合に未定義の振る舞いを避けるためには、シグナルを送る _at_thread_exit
関数で切り離されたスレッドを (手動で) 結合するか シグナルハンドラにとっても安全なコードだけを切り離したスレッドで 実行するようにする必要があります。
糸を取り外す。
std::thread::detach
](http://en.cppreference.com/w/cpp/thread/thread/detach) によると。
スレッドオブジェクトから実行スレッドを切り離し、独立した実行を可能にします。 実行スレッドをスレッドオブジェクトから分離し、独立した実行を継続できるようにします。割り当てられたリソースはすべて スレッドが終了すると、解放されます。
pthread_detach`](http://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_detach.html) より。
pthread_detach() 関数は実装に対して、そのスレッドが終了したときにスレッドのストレージを再利用できることを示さなければならない。 pthread_detach() 関数は、そのスレッドが終了したときに、そのスレッドのストレージを取り戻すことができることを実装に示さなければならない。 終了したときに、スレッドのストレージを取り戻すことができることを実装に示さなければならない。スレッドが終了していない場合、pthread_detach()はスレッドを終了させてはならない。 スレッドが終了していない場合、pthread_detach() はスレッドを終了させてはならない > 。同じスレッドに対して複数の pthread_detach() を呼び出した場合の影響。 を複数回呼び出した場合の効果は未定です。
スレッドの切り離しは、主にアプリケーションがスレッドの終了を待つ必要がない場合(例えば、プロセス終了まで実行しなければならないデーモンなど)に、リソースを節約するために行われる。
1.アプリケーション側のハンドルを解放する。1. アプリケーション側のハンドルを解放する: std::thread
オブジェクトを参加させずにスコープ外に出すことができます。通常は破壊時に std::terminate()
を呼び出すことになりますが、このようなことがなくなります。
2.2. スレッドが終了すると同時に、OS がスレッド固有の資源 (TCB) を自動的にクリーンアップするようにすることです。
スレッドを終了させる
プロセス終了時の動作はメインスレッドと同じで、少なくともいくつかのシグナルをキャッチすることができます。他のスレッドがシグナルを処理できるかどうかはそれほど重要ではなく、メインスレッド'のシグナルハンドラ呼び出しの中で他のスレッドに参加したり終了させたりすることができるのです。(関連質問)
すでに述べたように、どのスレッドも、デタッチされていようがいまいが、ほとんどのOSではそのプロセスとともに死ぬのです。プロセス自体を終了させるには、シグナルを送出するか、exit()
を呼び出すか、main関数からリターンする方法があります。しかし、C++11では、基盤となるOSの正確な動作を定義することはできませんし、定義しようともしません。一方、Java VMの開発者は、そのような違いをある程度抽象化できることは確かです。古いプラットフォーム(C++11が移植されることはないでしょう)や様々な組み込みシステムには、特殊な言語ライブラリの実装や限られた言語サポートがあります。
スレッドサポート
スレッドがサポートされていない場合 std::thread::get_id()
は無効な ID (デフォルトでは std::thread::id
) を返さなければなりません。これが、私がC++11を今日のOSと組み合わせて理解する方法です。もし、スレッドをサポートしているOSで、プロセス中にメインスレッドを生成しないものがあれば、教えてください。
スレッドを適切にシャットダウンするために、スレッドを制御する必要がある場合、同期プリミティブやある種のフラグを使用することでそれを行うことができます。しかし、この場合、シャットダウンフラグを設定した後にジョインするのが私の好む方法です。スレッドを切り離すことによって複雑さが増すことは意味がなく、リソースはどのみち同時に解放されるので、std::thread
オブジェクトの数バイトと、より複雑でおそらくより多くの同期プリミティブを比較すれば受け入れられるでしょう。
プログラムが終了した後のスレッドの運命は未定義の動作である。しかし、最近のオペレーティングシステムは、プロセスを閉じる際に、そのプロセスによって生成されたすべてのスレッドをクリーンアップします。
std::thread` をデタッチする際には、これらの3つの条件が継続されます。
*this
はもはやどのスレッドも所有していない。joinable()
は常に false
と等しくなる。get_id()
は std::thread::id()
と等しくなる。