Ich schreibe eine Multithread-C ++ - Anwendung für * nix-Betriebssysteme. Welche Best Practices zum ordnungsgemäßen Beenden einer solchen Anwendung gibt es? Mein Instinkt ist, dass ich einen Signalhandler auf SIGINT (SIGTERM?) Installieren möchte, der meine Threads stoppt / verbindet. Ist es auch möglich zu "garantieren", dass alle Destruktoren aufgerufen werden (vorausgesetzt, dass keine anderen Fehler oder Ausnahmen während der Behandlung des Signals ausgelöst werden)?
Einige Überlegungen fallen mir ein:
bezeichnet einen Thread, der für die Orchestrierung des Herunterfahrens zuständig ist, z. B. wie Dithermaster vorgeschlagen hat, dies könnte der Hauptthread sein, wenn Sie eine eigenständige Anwendung schreiben. Oder wenn Sie eine Bibliothek schreiben, stellen Sie eine Schnittstelle (zB Funktionsaufruf) zur Verfügung, mit der ein Client-Programm die in der Bibliothek erstellten Objekte terminieren kann.
Sie können nicht garantieren, dass Destruktoren aufgerufen werden; Das liegt an Ihnen und erfordert vorsichtiges Löschen für jedes neue. Vielleicht helfen dir intelligente Zeiger. Aber das ist wirklich eine Designüberlegung. Die Hauptkomponenten sollten Start & amp; Stop-Semantik, die Sie aus dem Klassenkonstruktor & amp; Destruktor.
Die Abschaltsequenz für eine Menge interagierender Objekte ist etwas, das einige Anstrengungen erfordern kann, um korrekt zu werden. Zum Beispiel, bevor Sie ein Objekt löschen, sind Sie sicher, dass ein Timer-Mechanismus nicht versucht, es in wenigen Mikro / Millisekunden später aufzurufen? Versuch und Irrtum ist dein Freund hier; einen Rahmen entwickeln, der wiederholt & amp; Starten und stoppen Sie Ihre Anwendung schnell, um heruntergefahrene Rennbedingungen zu erkennen.
Signale sind eine Möglichkeit, ein Ereignis auszulösen; Andere wiederum suchen regelmäßig nach einer bekannten Datei oder öffnen einen Socket und empfangen Daten. In jedem Fall möchten Sie den Code für die Abschaltsequenz vom Triggerereignis entkoppeln.
Meine Empfehlung ist, dass der Hauptthread alle Worker-Threads beendet, bevor er sich selbst beendet. Senden Sie jedem Mitarbeiter ein Ereignis, in dem er es aufräumt und beendet, und warten Sie, bis jeder dies getan hat. Dadurch können alle C ++ Destruktoren ausgeführt werden.
In Bezug auf die Signalverwaltung ist das einzige, was Sie portabel und sicher in einem Signal-Handler tun können, in eine Variable vom Typ% zu schreiben. co_de% (möglicherweise sig_atomic_t
-qualifiziert) und zurückgeben. Im Allgemeinen können Sie die meisten Funktionen nicht aufrufen und darf nicht in den globalen Speicher schreiben. Mit anderen Worten, der Handler sollte einfach ein Flag setzen, das in Ihrer Hauptroutine getestet wird, an einem Punkt, den Sie für angemessen halten, und die Aktion, die sich aus dem Signal selbst ergibt, sollte von dort aus ausgeführt werden.
(Da es möglicherweise blockierende E / A-Vorgänge gibt, sollten Sie POSIX-Thread-Löschung studieren. Ihr Unix-Klon (vor allem Linux) könnte Besonderheiten in Bezug auf diese und die oben genannten haben.)
In Bezug auf Destruktoren ist keine Magie involviert. Sie werden ausgeführt, wenn die Kontrolle einen gegebenen Umfang durch irgendwelche in der Sprache definierten Mittel verlässt. Das Verlassen eines Bereichs durch andere Methoden (zum Beispiel volatile
oder sogar longjmp()
) löst keine Destruktoren aus.
In Bezug auf allgemeine Stilllegungspraktiken gibt es unterschiedliche Meinungen zu diesem Thema.
Einige geben an, dass eine "anmutige Beendigung" im Sinne der Freigabe jeder Ressource, die jemals zugewiesen wurde, ausgeführt werden sollte. In C ++ bedeutet dies normalerweise, dass alle Destruktoren ordnungsgemäß ausgeführt werden müssen, bevor der Prozess beendet wird. Dies ist in der Praxis schwierig und oft eine Quelle von viel Leid, insbesondere in Multithread-Programmen, aus einer Vielzahl von Gründen. Signale verkomplizieren die Dinge zusätzlich durch die Art der asynchronen Signalverteilung.
Da der Großteil dieser Arbeit völlig nutzlos ist, behaupten einige andere, wie ich, dass das Programm sofort beendet werden muss, möglicherweise kurz nachdem dauerhafte Änderungen am System rückgängig gemacht werden (z. B. temporäre Dateien entfernen oder Bildschirmauflösung wiederherstellen) und die Konfiguration gespeichert wird . Eine scheinbar aufgeräumte Bereinigung ist nicht nur eine Zeitverschwendung (weil das Betriebssystem die meisten Dinge wie zugeteilten Speicher, freie Threads und offene Dateideskriptoren bereinigen wird), sondern könnte auch eine ernste Zeitverschwendung sein (Freigabe von Zuweisungen) könnte ausgelagerten Speicher berühren, das System nutzlos dazu zwingen, sie einfach nach dem Beenden des Prozesses freizugeben, um sie freizugeben, ohne die Möglichkeit zu erwähnen, dass Deadlocks durch das Verknüpfen von Threads entstehen.
Sag einfach nein. Wenn du gehen willst, rufe exit()
(oder sogar exit()
auf, aber pass auf unflushed I / O auf) und das war's. Ärger als langsame Startprogramme sind langsame Programme zu beenden.
Tags und Links c++ signals unix application-shutdown