main()が終了すると、切り離されたスレッドはどうなりますか?


152

私はそれを開始しているstd::threadと仮定しdetach()て、それstd::threadが一度それを表していたとしても、スレッドがスコープ外になったとしても、スレッドは実行を続けます。

さらに、プログラムには、切り離されたスレッド1に参加するための信頼できるプロトコルがないため、main()終了時に切り離されたスレッドが引き続き実行されると仮定します。

1.10と30.3のどちらにも適切な表現が含まれておらず、何が起こるかを説明している標準(より正確には、N3797 C ++ 14ドラフト)には何も見つかりません。

1別の、おそらく同等の質問は、「デタッチされたスレッドを再び結合できるか」です。これは、どのプロトコルを結合しようと考えている場合でも、スレッドがまだ実行されている間にシグナリング部分を実行する必要があるため、OSスケジューラが受信側がスレッドが実際に終了したことを確実に検出する方法がなく、シグナリングが実行された直後に、スレッドを1時間スリープさせることにしました。

が不足している場合はmain()デタッチスレッドが実行されているとすると、未定義の動作で、その後、任意の使用は、std::thread::detach()メインスレッドが終了したことがない場合を除き、未定義の動作です2

したがって、main()デタッチされたスレッドを実行して実行すると、定義された効果が必要になります。問題は次のとおりですC ++標準では、POSIXではなく、OSドキュメントではありません...)定義されている効果です。

2切り離されたスレッドは結合できません(の意味でstd::thread::join())。デタッチされたスレッドからの結果待つことはできます(たとえば、からstd::packaged_task、またはカウントセマフォまたはフラグと条件変数によって)、それはスレッドが実行を完了したことを保証するものではありません。あなたがスレッドの最初の自動オブジェクトのデストラクタにシグナリング部分を入れない限り、確かに、そこになり、一般的には、その実行コード(デストラクタ)も後のシグナリングコード。OSがメインスレッドをスケジュールして結果を消費し、デタッチされたスレッドがデストラクタの実行を完了する前に終了する場合、^ Wisはどのように定義されますか?


5
私は[basic.start.term] / 4に非常に漠然とした非必須のノートを見つけることができます:「への呼び出しの前にすべてのスレッドの終了std::exitまたはから出るmainこれらの要求を満たすために、必要十分な、しかし、ではありません。」(段落全体が関連する場合があります)[support.start.term] / 8(戻りstd::exit時に呼び出される)も参照してくださいmain
dyp

回答:


45

元の質問「main()終了時にデタッチされたスレッドはどうなるか」に対する答えは次のとおりです。

他のスレッドの(automatic | thread_local)変数にも静的オブジェクトにも触れない限り、実行は継続され(標準では停止とは言われていないため)、明確に定義されています。

これは、スレッドマネージャーを静的オブジェクトとして許可することが許可されているようです([basic.start.term] / 4の注記は、ポインターの@dypのおかげで十分に言えます)。

静的オブジェクトの破棄が終了すると問題が発生します。それは、シグナルハンドラーで許可されたコードのみが実行される可能性がある領域に実行が入るためです([basic.start.term] / 1、1番目の文)。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シグナルを送信する関数と結合する、切り離されたスレッドに安全なコードのみを実行させる必要がありますシグナルハンドラも。


17
あなたはこれについて確信を持っていますか?私がテストしたすべての場所(GCC 5、clang 3.5、MSVC 14)では、メインスレッドが終了すると、切り離されたすべてのスレッドが強制終了されます。
rustyx 2016年

3
問題は特定の実装が行うことではなく、標準が未定義の動作として定義するものを回避する方法であると私は信じています。
Jon Spencer

7
この回答は、静的変数の破棄後、プロセスが残りのスレッドが完了するのを待つ何らかのスリープ状態になることを示唆しているようです。それは後に、真実ではないexit仕上げが実行して、静的オブジェクトを破壊するatexit、すなわち、それはホスト環境に制御を返すなど、プロセスの終了をストリームをフラッシュし、ハンドラを。デタッチされたスレッドがまだ実行中の場合(そして、独自のスレッドの外部に触れないことで未定義の動作を何らかの形で回避した場合)、プロセスが終了すると、そのスレッドは一気に消えます。
Jonathan Wakely

3
ISO以外のC ++ APIを使用して問題がなければ、リターンやmain呼び出しのpthread_exit代わりに呼び出しexitを行うと、プロセスは切り離されたスレッドの終了を待機しexit、最後のスレッドが終了した後に呼び出します。
ジョナサンウェイクリー

3
"(標準では停止されているとは表示されないため)実行を継続します"->スレッドがそのコンテナープロセスの間どのように実行を継続できるかを誰かに教えてもらえますか?
Gupta

42

スレッドの切り離し

によるとstd::thread::detach

実行のスレッドをスレッドオブジェクトから分離し、独立して実行を継続できるようにします。割り当てられたリソースは、スレッドが終了すると解放されます。

からpthread_detach

pthread_detach()関数は、そのスレッドが終了すると、スレッドのストレージを再利用できることを実装に示します。スレッドが終了していない場合、pthread_detach()はスレッドを終了させません。同じターゲットスレッドに対する複数のpthread_detach()呼び出しの影響は規定されていません。

スレッドのデタッチは、アプリケーションがスレッドの終了を待機する必要がない場合に主にリソースを節約するためです(たとえば、デーモンは、プロセスの終了まで実行する必要があります)。

  1. アプリケーション側のハンドルを解放するには:std::thread結合せずにオブジェクトをスコープから外すことができます。これにより、通常std::terminate()、破棄時にが呼び出されます。
  2. 明示的に指定したため、スレッドが終了するとすぐにOSがスレッド固有のリソース(TCB)を自動的にクリーンアップできるようにするため、後でスレッドに参加する必要はないため、すでに切り離されたスレッドに参加することはできません。

スレッドの強制終了

プロセス終了時の動作は、少なくとも一部のシグナルをキャッチできるメインスレッドの動作と同じです。他のスレッドがシグナルを処理できるかどうかはそれほど重要ではありません。メインスレッドのシグナルハンドラー呼び出し内で他のスレッドを結合または終了できるからです。(関連質問

すでに述べたように、スレッドは、切り離されているかどうかに関係なく、ほとんどのOSでそのプロセスによって停止します。プロセス自体は、シグナルを発信するexit()か、メイン関数を呼び出すか、メイン関数から戻ることによって終了できます。ただし、C ++ 11では、基盤となるOSの正確な動作を定義することはできません。その一方、Java VMの開発者は、そのような違いをある程度まで確実に抽象化できます。AFAIK、エキゾチックプロセス、およびスレッドモデルは通常、古代のプラットフォーム(C ++ 11はおそらく移植されない)およびさまざまな組み込みシステムで見られます。これらのシステムには、特別または制限された言語ライブラリの実装があり、言語サポートも制限されている可能性があります。

スレッドのサポート

スレッドがサポートされていない場合は、実行するスレッドオブジェクトを必要としないプレーンプロセスがあり、のコンストラクターがをスローするstd::thread::get_id()必要があるため、無効なID(デフォルトではstd::thread::id)を返す必要があります。これが、今日のOSと組み合わせてC ++ 11を理解する方法です。プロセスにメインスレッドを生成しない、スレッドサポート付きのOSがある場合は、お知らせください。std::threadstd::system_error

スレッドの制御

適切にシャットダウンするためにスレッドを制御し続ける必要がある場合は、同期プリミティブやある種のフラグを使用してそれを行うことができます。ただし、この場合、シャットダウンフラグの後に結合を設定するのが私が好む方法です。std::threadオブジェクトの数バイトの場所でリソースが同時に解放されるため、スレッドを切り離して複雑さを増す意味はありません。複雑性とより多くの同期プリミティブが許容されるはずです。


3
すべてのスレッドには独自のスタック(Linuxではメガバイトの範囲内)があるため、スレッドを切り離し(スレッドが終了するとすぐにスタックが解放されるようにする)、メインスレッドを終了する必要がある場合はいくつかの同期プリミティブを使用します(そして適切なシャットダウンのためには、戻り/終了時にそれらを終了するのではなく、まだ実行中のスレッドに参加する必要があります)。
NorbertBérci2014年

8
これがどのように質問に答えるかは本当にわかりません
MikeMB '27

18

次のコードを検討してください。

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void thread_fn() {
  std::this_thread::sleep_for (std::chrono::seconds(1)); 
  std::cout << "Inside thread function\n";   
}

int main()
{
    std::thread t1(thread_fn);
    t1.detach();

    return 0; 
}

Linuxシステムで実行すると、thread_fnからのメッセージは出力されません。確かに、OS thread_fn()main()終了するとすぐにクリーンアップします。で置き換えるt1.detach()t1.join()、メッセージは期待どおりに印刷されます。


この動作はWindowsでのみ発生します。したがって、プログラムが終了すると、切り離されたスレッドがWindowsによって強制終了されるようです。
Gupta

17

プログラムが終了した後のスレッドの運命は、未定義の動作です。しかし、最近のオペレーティングシステムは、プロセスを閉じるときに、プロセスによって作成されたすべてのスレッドをクリーンアップします。

を切り離すときstd::thread、これらの3つの条件は引き続き保持されます。

  1. *this もはやスレッドを所有していません
  2. joinable() 常に等しい false
  3. get_id() 等しい std::thread::id()

1
なぜ未定義なのですか?標準は何も定義していないのですか?私の脚注で、それdetach()は未定義の振る舞いを持つように呼びかけませんか?信じがたい...
マルク・ムッツ-mmutz 2013年

2
@ MarcMutz-mmutzプロセスが終了した場合にスレッドの運命が定義されていないという意味では、定義されていません。
シーザー

2
@Caesarと、スレッドが完了する前に終了しないようにするにはどうすればよいですか?
MichalH 2016


0

他のスレッドが実行を継続できるようにするには、メインスレッドは、exit(3)ではなくpthread_exit()を呼び出して終了する必要があります。メインでpthread_exitを使用しても問題ありません。pthread_exitを使用すると、メインスレッドは実行を停止し、他のすべてのスレッドが終了するまでzombie(defunct)ステータスのままになります。メインスレッドでpthread_exitを使用している場合、他のスレッドの戻りステータスを取得できず、他のスレッドのクリーンアップを実行できません(pthread_join(3)を使用して行うことができます)。また、スレッドを切り離すと(pthread_detach(3))、スレッドのリソースがスレッドの終了時に自動的に解放されるようにします。共有リソースは、すべてのスレッドが終了するまで解放されません。


@kgvinod、なぜ「pthread_exit(0);」を追加しないのか 「ti.detach()」の後;
yshi

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.