web-dev-qa-db-ja.com

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

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

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

何が起こるべきかを説明する標準(より正確には、N3797 C++ 14ドラフト)には何も見つかりません。1.10も30.3も適切な文言を含んでいません。

1 おそらく同等の別の質問は、「切り離されたスレッドを再び参加させることができます」ということです。参加しようと考えているプロトコルが何であれ、スレッドの実行中にシグナリング部分を実行する必要があり、OSスケジューラが決定する可能性があるためですシグナルが実行された直後にスレッドを1時間スリープさせ、受信側がスレッドが実際に終了したことを確実に検出する方法がありません。

分離されたスレッドが実行されているmain()が不足している場合、anyメインスレッドが終了しない限り、std::thread::detach()の使用は未定義の動作です2

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

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

137

元の質問「main()が終了したときに切り離されたスレッドに何が起こるか」に対する答えは次のとおりです。

他のスレッドの(automatic | thread_local)変数や静的オブジェクトのいずれにも触れない限り、それは実行を継続します(標準では停止しているとは書かれていないため)。

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

静的オブジェクトの破壊が終了すると、問題が発生します。これは、実行がシグナルハンドラで許可されているコードのみが実行できるレジームに入るためです([basic.start.term]/1、1番目の文)。 C++標準ライブラリのうち、これは<atomic>ライブラリのみです([support.runtime]/9、2番目の文)。特に、一般的には、-excludescondition_variable<atomic>の一部ではないため、シグナルハンドラで使用するために保存するかどうかは実装で定義されます)。

この時点でスタックを解いていない限り、未定義の動作を回避する方法を理解するのは困難です。

2番目の質問「切り離されたスレッドを再び結合できるか」に対する答えは次のとおりです。

はい、*_at_thread_exitファミリーの関数(notify_all_at_thread_exit()std::promise::set_value_at_thread_exit()、...)を使用します。

質問の脚注[2]に記載されているように、条件変数、セマフォ、またはアトミックカウンタのシグナルは、切り離されたスレッドに参加するには不十分です(実行の終了を保証するという意味でhas-happened- before待機スレッドによる前記シグナルの受信)、一般に、例えば条件変数のnotify_all()、特に自動およびスレッドローカルオブジェクトのデストラクタ。

スレッドが最後に行うこととしてシグナリングを実行する(after自動およびスレッドローカルオブジェクトのデストラクターhas-happened)は、_at_thread_exitファミリーの関数でしたのために設計されています。

そのため、標準が必要とする以上の実装保証がない場合の未定義の動作を回避するために、シグナリングを行う_at_thread_exit関数で(手動で)切り離されたスレッドを結合する必要がありますまたは切り離されたスレッドを実行しますのみシグナルハンドラーにとっても安全なコード。

43

スレッドの切り離し

std::thread::detach によると:

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

From 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)を返す必要がありますまた、 std::thread のコンストラクターは std::system_error をスローする必要があります。これが、今日のOSとともにC++ 11を理解する方法です。プロセスにメインスレッドを生成しないスレッドサポートを備えたOSがある場合は、お知らせください。

スレッドの制御

適切にシャットダウンするためにスレッドの制御を維持する必要がある場合は、同期プリミティブや何らかのフラグを使用してそれを行うことができます。ただし、この場合、シャットダウンフラグに続いて結合を設定することは、スレッドを切り離すことによって複雑さを増す意味がないため、とにかくリソースが同時に解放されるため、 std::thread オブジェクトと、より高い複雑さ、場合によってはより多くの同期プリミティブが許容されるはずです。

40
Sam

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

std::threadをデタッチすると、これら3つの条件は保持され続けます

  1. *thisはスレッドを所有しなくなりました
  2. joinable()は常にfalseになります
  3. get_id()はstd :: thread :: id()と等しくなります
17
Caesar

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

#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()に置き換えると、常に期待どおりにメッセージが出力されます。

15
kgvinod

メインスレッド(つまり、main()関数を実行するスレッド)が終了すると、プロセスが終了し、他のすべてのスレッドが停止します。

リファレンス: https://stackoverflow.com/a/4667273/219484

5
funk

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

0
yshi