Angenommen, ich starte a std::thread
und dann detach()
es, sodass der Thread weiterhin ausgeführt wird, obwohl der Thread, der std::thread
ihn einmal dargestellt hat, außerhalb des Gültigkeitsbereichs liegt.
Angenommen, das Programm verfügt nicht über ein zuverlässiges Protokoll zum Verbinden des getrennten Threads 1 , sodass der getrennte Thread beim main()
Beenden weiterhin ausgeführt wird.
Ich kann im Standard (genauer gesagt im Entwurf von N3797 C ++ 14) nichts finden, was beschreibt, was passieren soll. Weder 1.10 noch 30.3 enthalten relevante Formulierungen.
1 Eine andere, wahrscheinlich äquivalente Frage lautet: "Kann ein losgelöster Thread jemals wieder verbunden werden?" Entscheiden Sie sich, den Thread kurz nach der Signalisierung für eine Stunde in den Ruhezustand zu versetzen, ohne dass das empfangende Ende zuverlässig erkennen kann, dass der Thread tatsächlich beendet wurde.
Wenn das Auslaufen main()
mit getrennten Threads ein undefiniertes Verhalten ist, ist jede Verwendung von std::thread::detach()
undefiniertem Verhalten, es sei denn, der Hauptthread wird nie beendet 2 .
Das Auslaufen main()
mit abgelösten Threads muss daher definierte Auswirkungen haben. Die Frage ist: Wo (im C ++ - Standard nicht POSIX, nicht OS-Dokumente, ...) sind diese Effekte definiert.
2 Ein losgelöster Faden kann nicht verbunden werden (im Sinne von std::thread::join()
). Sie können auf Ergebnisse von getrennten Threads warten (z. B. über eine Zukunft von std::packaged_task
oder durch ein Zählsemaphor oder ein Flag und eine Bedingungsvariable), dies garantiert jedoch nicht, dass die Ausführung des Threads abgeschlossen ist . Wenn Sie den Signalisierungsteil nicht in den Destruktor des ersten automatischen Objekts des Threads einfügen , gibt es im Allgemeinen Code (Destruktoren), der nach dem Signalisierungscode ausgeführt wird. Wenn das Betriebssystem plant, dass der Hauptthread das Ergebnis verbraucht und beendet wird, bevor der getrennte Thread die Ausführung der Destruktoren beendet, was wird dann definiert?
std::exit
oder dem Verlassen vonmain
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
wird aufgerufen, wennmain
zurückgegeben wird)Antworten:
Die Antwort auf die ursprüngliche Frage "Was passiert mit einem getrennten Thread beim
main()
Beenden?" Lautet:Es läuft weiter (weil der Standard nicht sagt, dass es gestoppt ist), und das ist gut definiert, solange es weder (automatische | thread_local) Variablen anderer Threads noch statische Objekte berührt.
Dies scheint erlaubt zu sein, Thread-Manager als statische Objekte zuzulassen (Hinweis in [basic.start.term] / 4 sagt dies dank @dyp für den Zeiger).
Probleme treten auf, wenn die Zerstörung statischer Objekte abgeschlossen ist, da dann die Ausführung in ein Regime eintritt, in dem nur Code ausgeführt werden darf, der in Signalhandlern zulässig ist ( [basic.start.term] / 1, 1. Satz ). Von der C ++ - Standardbibliothek ist dies nur die
<atomic>
Bibliothek ( [support.runtime] / 9, 2. Satz ). Insbesondere schließtcondition_variable
dies im Allgemeinen aus (es ist implementierungsdefiniert, ob dies für die Verwendung in einem Signalhandler gespeichert ist, da es nicht Teil von ist<atomic>
).Wenn Sie Ihren Stapel zu diesem Zeitpunkt nicht abgewickelt haben, ist es schwer zu erkennen, wie Sie undefiniertes Verhalten vermeiden können.
Die Antwort auf die zweite Frage "Können getrennte Threads jemals wieder verbunden 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 [2] der Frage erwähnt, reicht das Signalisieren einer Bedingungsvariablen oder eines Semaphors oder eines Atomzählers nicht aus, um sich einem getrennten Thread anzuschließen (in dem Sinne, dass das Ende seiner Ausführung vor dem Empfang von stattgefunden hat) die Signalisierung durch einen wartenden Thread), da im Allgemeinen mehr Code ausgeführt wird, z. B.
notify_all()
nach einer Bedingungsvariablen, insbesondere nach den Destruktoren von automatischen und threadlokalen Objekten.Das Ausführen der Signalisierung als letztes, was der Thread tut ( nachdem Destruktoren von automatischen und threadlokalen Objekten aufgetreten sind ), ist das, wofür die
_at_thread_exit
Funktionsfamilie entwickelt wurde.Um undefiniertes Verhalten zu vermeiden, wenn keine Implementierungsgarantien über den Standardanforderungen liegen, müssen Sie einem getrennten Thread (manuell) eine
_at_thread_exit
Funktion zur Signalisierung hinzufügen oder den getrennten Thread nur Code ausführen lassen , für den dies sicher wäre auch ein Signalhandler.quelle
exit
Abschluss der Zerstörung statischer Objekte, Ausführen vonatexit
Handlern, Löschen von Streams usw. wird die Kontrolle an die Hostumgebung zurückgegeben, dh der Prozess wird beendet. Wenn ein losgelöster Thread noch läuft (und irgendwie undefiniertes Verhalten vermieden hat, indem nichts außerhalb seines eigenen Threads berührt wurde), verschwindet er einfach in einer Rauchwolke, wenn der Prozess beendet wird.main
Aufrufepthread_exit
anstelle von Rückgabe oder Aufruf ausgeführtexit
werden, darauf warten, dass getrennte Threads beendet werden, und dannexit
nach Abschluss des letzten Threads aufgerufen .Fäden abnehmen
Laut
std::thread::detach
:Von
pthread_detach
:Das Trennen von Threads dient hauptsächlich zum Einsparen von Ressourcen, falls die Anwendung nicht auf das Beenden eines Threads warten muss (z. B. Daemons, die bis zur Beendigung des Prozesses ausgeführt werden müssen):
std::thread
Objekt außerhalb des Bereichs verlassen, ohne sich zu verbinden, was normalerweise zu einem Aufruf zurstd::terminate()
Zerstörung führt.Fäden töten
Das Verhalten beim Beenden des Prozesses ist das gleiche wie beim Hauptthread, der zumindest einige Signale abfangen könnte. Ob andere Threads Signale verarbeiten können oder nicht, ist nicht so wichtig, da andere Threads innerhalb des Signalhandler-Aufrufs des Haupt-Threads verbunden oder beendet werden können. (Verwandte Frage )
Wie bereits erwähnt, stirbt jeder Thread, unabhängig davon, ob er getrennt ist oder nicht, auf den meisten Betriebssystemen . Der Prozess selbst kann durch Auslösen eines Signals, Aufrufen
exit()
oder Zurückkehren von der Hauptfunktion beendet werden. C ++ 11 kann und will jedoch nicht versuchen, das genaue Verhalten des zugrunde liegenden Betriebssystems zu definieren, während die Entwickler einer Java-VM solche Unterschiede sicherlich bis zu einem gewissen Grad 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 Implementierung der Sprachbibliothek und auch eingeschränkte Sprachunterstützung aufweisen könnten.Thread-Unterstützung
Wenn Threads nicht unterstützt werden,
std::thread::get_id()
sollte eine ungültige ID (standardmäßig erstelltstd::thread::id
) zurückgegeben werden, da es einen einfachen Prozess gibt, für dessen Ausführung kein Thread-Objekt erforderlich ist und der Konstruktor von a einestd::thread
auslösen solltestd::system_error
. So verstehe ich C ++ 11 in Verbindung mit den 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 steuern
Wenn Sie die Kontrolle über einen Thread behalten müssen, um ihn ordnungsgemäß herunterzufahren, können Sie dies mithilfe von Synchronisierungsprimitiven und / oder einer Art von Flags tun. In diesem Fall bevorzuge ich jedoch das Setzen eines Shutdown-Flags gefolgt von einem Join, da es keinen Sinn macht, die Komplexität durch Trennen von Threads zu erhöhen, da die Ressourcen ohnehin gleichzeitig freigegeben würden, wenn die wenigen Bytes des
std::thread
Objekts vorhanden wären Eine höhere Komplexität und möglicherweise mehr Synchronisationsprimitive sollten akzeptabel sein.quelle
Betrachten Sie den folgenden Code:
Wenn Sie es auf einem Linux-System ausführen, wird die Nachricht von thread_fn nie gedruckt. Das Betriebssystem wird tatsächlich bereinigt,
thread_fn()
sobald es beendet wirdmain()
. Durch Ersetzent1.detach()
durch wirdt1.join()
die Nachricht immer wie erwartet gedruckt.quelle
Das Schicksal des Threads nach dem Beenden des Programms ist undefiniertes Verhalten. Ein modernes Betriebssystem bereinigt jedoch alle Threads, die durch den Prozess beim Schließen erstellt wurden.
Beim Abnehmen von bleiben
std::thread
diese drei Bedingungen weiterhin gültig:*this
besitzt keinen Thread mehrjoinable()
wird immer gleich seinfalse
get_id()
wird gleich seinstd::thread::id()
quelle
detach()
undefiniertem Verhalten? Kaum zu glauben ...Wenn der Hauptthread (dh der Thread, der die Funktion main () ausführt) beendet wird, wird der Prozess beendet und alle anderen Threads werden gestoppt.
Referenz: https://stackoverflow.com/a/4667273/2194843
quelle
Damit andere Threads die Ausführung fortsetzen können, sollte der Hauptthread durch Aufrufen von pthread_exit () und nicht von exit (3) beendet werden. Es ist in Ordnung, pthread_exit in main zu verwenden. Wenn pthread_exit verwendet wird, wird der Hauptthread nicht mehr ausgeführt und bleibt im Zombie-Status (nicht mehr aktiv), bis alle anderen Threads beendet werden. Wenn Sie pthread_exit im Hauptthread verwenden, den Rückgabestatus anderer Threads nicht abrufen und keine Bereinigung für andere Threads durchführen können (dies kann mit pthread_join (3) erfolgen). Außerdem ist es besser, Threads (pthread_detach (3)) zu trennen, damit Thread-Ressourcen beim Beenden des Threads automatisch freigegeben werden. Die freigegebenen Ressourcen werden erst freigegeben, wenn alle Threads beendet sind.
quelle