Was passiert mit einem getrennten Thread, wenn main() beendet wird?

Lesezeit: 12 Minuten

Was passiert mit einem getrennten Thread wenn main beendet wird
Marc Mutz – mmutz

Angenommen, ich beginne eine std::thread und dann detach() es, sodass der Thread weiterhin ausgeführt wird, obwohl die std::thread das es einst darstellte, geht aus dem Rahmen.

Nehmen Sie weiter an, dass das Programm kein zuverlässiges Protokoll zum Verbinden mit dem getrennten Thread hat1sodass der getrennte Thread weiterhin ausgeführt wird, wenn main() Ausgänge.

Ich kann im Standard (genauer gesagt im N3797 C++14 Entwurf) nichts finden, was beschreibt, was passieren soll, weder 1.10 noch 30.3 enthalten sachdienliche Formulierungen.

1 Eine andere, wahrscheinlich gleichwertige Frage lautet: “Kann ein getrennter Thread jemals wieder beigetreten werden”, denn unabhängig davon, welches Protokoll Sie erfinden, um beizutreten, der Signalisierungsteil müsste erfolgen, während der Thread noch läuft, und der OS-Scheduler könnte entscheiden den Thread für eine Stunde in den Ruhezustand zu versetzen, unmittelbar nachdem die Signalisierung durchgeführt wurde, ohne dass das empfangende Ende zuverlässig erkennen kann, dass der Thread tatsächlich beendet wurde.

Wenn ausgeht main() Das Ausführen von getrennten Threads ist dann ein undefiniertes Verhalten irgendein Gebrauch von std::thread::detach() ist ein undefiniertes Verhalten, es sei denn, der Haupt-Thread wird nie beendet2.

Somit läuft aus main() bei abgelösten Threads laufen müssen definiert Auswirkungen. Die Frage ist: wo (in dem C++-Standardnicht POSIX, nicht OS docs, …) sind diese Effekte definiert.

2 Ein abgetrennter Faden kann nicht verbunden werden (im Sinne von std::thread::join()). Sie kann auf Ergebnisse von abgelösten Threads warten (z. B. über eine Zukunft von std::packaged_taskoder durch eine Zählsemaphore oder ein Flag und eine Bedingungsvariable), aber das garantiert nicht, dass die Thread hat die Ausführung beendet. In der Tat, es sei denn, Sie fügen den Signalteil in den Destruktor des ersten automatischen Objekts des Threads ein Willeim Allgemeinen Code (Destruktoren), der ausgeführt wird nach dem der Signalcode. Wenn das Betriebssystem den Haupt-Thread so plant, dass er das Ergebnis verarbeitet und beendet, bevor der getrennte Thread die Ausführung der Destruktoren beendet, was soll dann passieren?

  • Ich kann darin nur einen sehr vagen nicht obligatorischen Hinweis finden [basic.start.term]/4: “Beende jeden Thread vor einem Aufruf von std::exit oder die Ausfahrt aus main ist ausreichend, aber nicht notwendig, um diese Anforderungen zu erfüllen.“ (der gesamte Absatz kann relevant sein) Siehe auch [support.start.term]/8 (std::exit heißt wann main kehrt zurück)

    – dyp

    2. November 2013 um 17:29 Uhr

Die Antwort auf die ursprüngliche Frage “was passiert wann mit einem abgetrennten Thread main() Ausgänge” ist:

Es läuft weiter (weil der Standard nicht sagt, dass es gestoppt wird), und das ist wohldefiniert, solange es weder (automatische|thread_local) Variablen anderer Threads noch statische Objekte berührt.

Dies scheint zulässig zu sein, um Thread-Manager als statische Objekte zuzulassen (Hinweis in [basic.start.term]/4 sagt so viel, danke an @dyp für den Hinweis).

Probleme treten auf, wenn die Zerstörung statischer Objekte abgeschlossen ist, da die Ausführung dann in ein Regime eintritt, in dem nur in Signal-Handlern zugelassener Code ausgeführt werden kann ([basic.start.term]/1, 1. Satz). Von der C++-Standardbibliothek ist das nur die <atomic> Bücherei ([support.runtime]/9, 2. Satz). Insbesondere das – im Allgemeinen –ausschließt condition_variable (Es ist implementierungsdefiniert, ob dies sicher in einem Signalhandler verwendet werden kann, da es nicht Teil davon ist <atomic>).

Wenn Sie Ihren Stack zu diesem Zeitpunkt nicht aufgelöst haben, ist es schwer zu sehen, wie Sie undefiniertes Verhalten vermeiden können.

Die Antwort auf die zweite Frage “können getrennte Threads jemals wieder zusammengeführt werden” lautet:

Ja, mit der *_at_thread_exit Familie von Funktionen (notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit()…).

Wie in Fußnote vermerkt [2] der Frage reicht das Signalisieren einer Bedingungsvariablen oder eines Semaphors oder eines atomaren Zählers nicht aus, um einem abgetrennten Thread beizutreten (in dem Sinne, dass sichergestellt wird, dass das Ende seiner Ausführung ist-schon-vorgekommen das Empfangen der Signalisierung durch einen wartenden Thread), da im Allgemeinen nach z. B. a mehr Code ausgeführt wird notify_all() einer Bedingungsvariablen, insbesondere die Destruktoren von automatischen und Thread-lokalen Objekten.

Ausführen der Signalisierung als letztes, was der Thread tut (nach dem Destruktoren von automatischen und Thread-lokalen Objekten ist passiert) ist, was die _at_thread_exit Funktionsfamilie entwickelt wurde.

Um also undefiniertes Verhalten zu vermeiden, wenn keine Implementierungsgarantien über das hinausgehen, was der Standard erfordert, müssen Sie einen getrennten Thread (manuell) mit einem verbinden _at_thread_exit Funktion, die die Signalisierung durchführt oder Lassen Sie den getrennten Thread ausführen nur Code, der auch für einen Signalhandler sicher wäre.

  • Bist du dir sicher? Überall, wo ich getestet habe (GCC 5, Clang 3.5, MSVC 14), werden alle abgetrennten Threads beendet, wenn der Hauptthread beendet wird.

    – rostig

    29. Juni 2016 um 19:57 Uhr

  • Ich glaube, dass das Problem nicht darin besteht, was eine bestimmte Implementierung tut, sondern wie man das vermeidet, was der Standard als undefiniertes Verhalten definiert.

    – Jon Spencer

    29. November 2016 um 1:40 Uhr

  • Diese Antwort scheint zu implizieren, dass der Prozess nach der Zerstörung statischer Variablen in eine Art Ruhezustand wechselt und darauf wartet, dass alle verbleibenden Threads beendet werden. Das ist nachher nicht wahr exit Beendet die Zerstörung statischer Objekte und läuft atexit Handler, Flush-Streams usw. gibt es die Kontrolle an die Host-Umgebung zurück, dh der Prozess wird beendet. Wenn ein abgetrennter Thread noch läuft (und undefiniertes Verhalten irgendwie vermieden hat, indem er nichts außerhalb seines eigenen Threads berührt hat), dann verschwindet er einfach in einer Rauchwolke, wenn der Prozess beendet wird.

    – Jonathan Wakely

    27. April 2018 um 11:44 Uhr

  • Wenn Sie mit der Verwendung von Nicht-ISO-C++-APIs einverstanden sind, dann if main Anrufe pthread_exit anstatt zurückzukehren oder anzurufen exit dann wird der Prozess veranlasst, auf das Ende getrennter Threads zu warten und dann aufzurufen exit nachdem der letzte fertig ist.

    – Jonathan Wakely

    27. April 2018 um 11:51 Uhr

  • “Es läuft weiter (weil der Standard nicht sagt, dass es gestoppt ist)” –> Kann mir jemand sagen, WIE ein Thread die Ausführung ohne seinen Containerprozess fortsetzen kann?

    – Gupta

    5. März 2019 um 8:04 Uhr

Fäden lösen

Entsprechend std::thread::detach:

Trennt den Ausführungsthread vom Threadobjekt, sodass die Ausführung unabhängig fortgesetzt werden kann. Alle zugewiesenen Ressourcen werden freigegeben, sobald der Thread beendet wird.

Von pthread_detach:

Die Funktion pthread_detach() soll der Implementierung anzeigen, dass Speicher für den Thread zurückgefordert werden kann, wenn dieser Thread endet. Wenn der Thread nicht beendet wurde, soll pthread_detach() ihn nicht beenden. Die Auswirkung mehrerer pthread_detach()-Aufrufe auf denselben Ziel-Thread ist nicht spezifiziert.

Das Trennen von Threads dient hauptsächlich dem Sparen von Ressourcen, falls die Anwendung nicht auf das Beenden eines Threads warten muss (z. B. Daemons, die bis zum Beenden des Prozesses ausgeführt werden müssen):

  1. Um den anwendungsseitigen Griff zu befreien: Man kann a std::thread Objekt gehen aus dem Geltungsbereich ohne beizutreten, was normalerweise zu einem Aufruf führt std::terminate() auf Zerstörung.
  2. Damit das Betriebssystem die Thread-spezifischen Ressourcen bereinigen kann (TCB) automatisch, sobald der Thread beendet wird, da wir ausdrücklich angegeben haben, dass wir später nicht daran interessiert sind, dem Thread beizutreten, sodass man einem bereits getrennten Thread nicht beitreten kann.

Threads töten

Das Verhalten beim Beenden des Prozesses ist das gleiche wie beim Haupt-Thread, der zumindest einige Signale abfangen konnte. Ob andere Threads Signale verarbeiten können oder nicht, ist nicht so wichtig, da man andere Threads innerhalb des Signal-Handler-Aufrufs des Haupt-Threads verbinden oder beenden könnte. (Verwandte Frage)

Wie zuvor schon gesagt, Jeder Thread, ob getrennt oder nicht, wird auf den meisten Betriebssystemen mit seinem Prozess sterben. Der Prozess selbst kann durch Setzen eines Signals, durch Aufrufen beendet werden exit() oder durch Rückkehr von der Hauptfunktion. Allerdings kann und versucht C++11 nicht, das genaue Verhalten des zugrunde liegenden Betriebssystems zu definieren, wohingegen die Entwickler einer Java VM solche Unterschiede sicherlich einigermaßen abstrahieren können. AFAIK, exotische Prozess- und Threading-Modelle finden sich normalerweise auf alten Plattformen (auf die C ++ 11 wahrscheinlich nicht portiert wird) und verschiedenen eingebetteten Systemen, die eine spezielle und / oder eingeschränkte Sprachbibliotheksimplementierung und auch eingeschränkte Sprachunterstützung haben könnten.

Thread-Unterstützung

Wenn Threads nicht unterstützt werden std::thread::get_id() sollte eine ungültige ID zurückgeben (standardmäßig konstruiert std::thread::id), da es einen einfachen Prozess gibt, der kein Thread-Objekt zum Ausführen benötigt, und der Konstruktor von a std::thread sollte a werfen std::system_error. So verstehe ich C++11 in Verbindung mit heutigen Betriebssystemen. Wenn es ein Betriebssystem mit Threading-Unterstützung gibt, das in seinen Prozessen keinen Haupt-Thread erzeugt, lassen Sie es mich wissen.

Threads kontrollieren

Wenn man die Kontrolle über einen Thread zum ordnungsgemäßen Herunterfahren behalten muss, kann man dies tun, indem man Sync-Primitive und/oder eine Art von Flags verwendet. In diesem Fall bevorzuge ich jedoch das Setzen eines Shutdown-Flags gefolgt von einem Join, da es keinen Sinn macht, die Komplexität durch das Trennen von Threads zu erhöhen, da die Ressourcen sowieso gleichzeitig freigegeben würden, wo die wenigen Bytes der std::thread Objekt gegenüber höherer Komplexität und möglicherweise mehr Sync-Primitiven sollten akzeptabel sein.

  • Da jeder Thread seinen eigenen Stack hat (der unter Linux im Megabyte-Bereich liegt), würde ich mich dafür entscheiden, den Thread zu trennen (so dass sein Stack freigegeben wird, sobald er beendet wird) und einige Synchronisierungsprimitive verwenden, wenn der Hauptthread beendet werden muss (und für ein ordnungsgemäßes Herunterfahren muss es den noch laufenden Threads beitreten, anstatt sie beim Zurückkehren/Beenden zu beenden).

    – Norbert Berci

    12. Juni 2014 um 2:06 Uhr


  • Ich verstehe wirklich nicht, wie dies die Frage beantwortet

    – MikeMB

    27. Januar 2017 um 0:19 Uhr

Betrachten Sie den folgenden Code:

#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; 
}

Wenn Sie es auf einem Linux-System ausführen, wird die Nachricht von thread_fn nie gedruckt. Das Betriebssystem räumt tatsächlich auf thread_fn() sobald main() Ausgänge. Ersetzen t1.detach() mit t1.join() druckt die Nachricht immer wie erwartet.

  • Dieses Verhalten tritt genau unter Windows auf. Es scheint also, dass Windows die getrennten Threads beendet, wenn das Programm beendet ist.

    – Gupta

    5. März 2019 um 14:02 Uhr

  • Ich habe versucht, auch mit getrenntem Thread und Elternteil in Datei zu schreiben, weil ich dachte, dass stdout möglicherweise nicht funktioniert, nachdem Elternteil fertig ist. Aber es wird auch nicht in die Datei geschrieben. also hast du recht.

    – Ashish Aggarwal

    2. Oktober 2020 um 10:04 Uhr

Was passiert mit einem getrennten Thread wenn main beendet wird
Caesar

Das Schicksal des Threads nach dem Beenden des Programms ist undefiniertes Verhalten. Ein modernes Betriebssystem bereinigt jedoch alle vom Prozess erstellten Threads beim Schließen.

Beim Ablösen einer std::threadwerden diese drei Bedingungen weiterhin gelten:

  1. *this besitzt keinen Thread mehr
  2. joinable() wird immer gleich sein false
  3. get_id() wird gleich std::thread::id()

1646931013 20 Was passiert mit einem getrennten Thread wenn main beendet wird
Funk

Wenn der Haupt-Thread (dh der Thread, der die main()-Funktion ausführt) beendet wird, wird der Prozess beendet und alle anderen Threads werden angehalten.

Referenz: https://stackoverflow.com/a/4667273/2194843

1646931013 239 Was passiert mit einem getrennten Thread wenn main beendet wird
Gemeinschaft

Damit andere Threads die Ausführung fortsetzen können, sollte der Haupt-Thread beendet werden, indem pthread_exit() statt exit(3) aufgerufen wird. Es ist in Ordnung, pthread_exit in main zu verwenden. Wenn pthread_exit verwendet wird, stoppt der Haupt-Thread die Ausführung und verbleibt im Zombie-Status (deunct), bis alle anderen Threads beendet werden. Wenn Sie pthread_exit im Haupt-Thread verwenden, können Sie den Rückgabestatus anderer Threads nicht erhalten und keine Bereinigung für andere Threads durchführen (könnte mit pthread_join(3) durchgeführt werden). Außerdem ist es besser, Threads zu trennen (pthread_detach(3)), damit Thread-Ressourcen automatisch bei Thread-Beendigung freigegeben werden. Die gemeinsam genutzten Ressourcen werden nicht freigegeben, bis alle Threads beendet sind.

Wenn der Hauptprozess beendet wird, werden auch alle von diesem Prozess erstellten Worker-Threads beendet. Also, wenn die main() zurückkehrt, bevor ein von ihm erstellter getrennter Thread die Ausführung abschließt, wird der getrennte Thread vom Betriebssystem beendet. Nehmen Sie dieses Beispiel:

void work(){
     this_thread::sleep_for(chrono::seconds(2));
     cout<<"Worker Thread Completed"<<endl;
}
int main(){
     thread t(work);
     t.detach();
     cout<<"Main Returning..."<<endl;
     return 0;
}

Im obigen Programm Worker Thread Completed wird nie gedruckt. Seit main kehrt vor der Verzögerung von 2 Sekunden im Worker-Thread zurück. Wenn wir jetzt den Code ein wenig ändern und vorher eine Verzögerung von mehr als 2 Sekunden hinzufügen main kehrt zurück. Wie:

void work(){
     this_thread::sleep_for(chrono::seconds(2));
     cout<<"Worker Thread Completed"<<endl;
}
int main(){
     thread t(work);
     t.detach();
     cout<<"Main Returning..."<<endl;
     this_thread::sleep_for(chrono::seconds(4));
     return 0;
}

Ausgabe

Main Returning...
Worker Thread Completed

Wenn nun ein Thread aus anderen Funktionen als erstellt wird main Der getrennte Thread bleibt am Leben, bis seine Ausführungen abgeschlossen sind, auch nachdem die Funktion zurückgegeben wurde. Zum Beispiel:

void child()
{
     this_thread::sleep_for(chrono::seconds(2));
     cout << "Worker Thread Completed" << endl;
}
void parent(){
     thread t(child);
     t.detach();
     cout<<"Parent Returning...\n";
     return;
}
int main()
{
     parent();
     cout<<"Main Waiting..."<<endl;
     this_thread::sleep_for(chrono::seconds(5));
}

Ausgabe

Parent Returning...
Main Waiting...
Worker Thread Completed

Eine Problemumgehung zu machen main auf einen getrennten Worker-Thread zu warten, bevor er zurückkehrt, ist zu verwenden condition_variable. Zum Beispiel:

#include <bits/stdc++.h>
using namespace std;
condition_variable cv;
mutex m;
void work(){
    this_thread::sleep_for(chrono::seconds(2));
    cout << "Worker Thread Completed" << endl;
    cv.notify_all();
}
int main(){
    thread t(work);
    t.detach();
    cout << "Main Returning..." << endl;
    unique_lock<mutex>ul(m);
    cv.wait(ul);
    return 0;
}

988490cookie-checkWas passiert mit einem getrennten Thread, wenn main() beendet wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy