Warum wird der Destruktor einer Zukunft von `std :: async` blockiert?

Beim Versuch, eine andere Stackoverflow-Frage zu beantworten, erkannte ich, dass dieses einfache C ++ 11-Snippet den aufrufenden Thread implizit blockiert:

std::async(std::launch::async, run_async_task) 

Für mich wäre das der kanonische C ++ 11 Weg gewesen, eine Aufgabe asynchron zu starten, ohne sich um das Ergebnis zu kümmern. Stattdessen muss man explizit einen Thread erstellen und lösen (siehe Antwort auf die erwähnte Frage), um dies zu erreichen.

Also hier ist meine Frage: Gibt es einen Grund in Bezug auf Sicherheit / Richtigkeit, dass der Destruktor von std::future blockiert werden muss? Wäre es nicht genug, wenn es nur auf get blockiert wird und ansonsten, wenn mich der Rückgabewert oder die Ausnahme nicht interessiert, ist es einfach Feuer und vergessen?

Blockierende Destruktoren von Futures, die von std :: async und von Threads zurückgegeben werden: Das ist ein kontroverses Thema. Die folgende chronologische Liste der Beiträge spiegelt einige der Diskussionen der Mitglieder des Ausschusses wider:

  • N2802: Ein Appell, die Loslösung von Fadenobjekten durch Hans Böhm zu überdenken
  • N3630: async, ~ future und ~ thread (Revision 1) von Herb Sutter
  • N3636: ~ thread Sollte von Herb Sutter beitreten
  • N3637: async und ~ future (Revision 3) von Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3679: Async () zukünftige Destruktoren müssen von Hans Böhm warten
  • N3773: async und ~ future (Revision 4) von Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3776: Formulierung für ~ Zukunft von Herb Sutter
  • N3777: Formulierung für die Ablehnung von Async von Herb Sutter

Obwohl es viele Diskussionen gab, sind für C ++ 14 keine Änderungen in Bezug auf das Blockierungsverhalten der Destruktoren von std :: future und std :: thread geplant .

Was Ihre Frage betrifft, ist die interessanteste Arbeit wahrscheinlich die zweite von Hans Böhm. Ich zitiere einige Teile, um deine Frage zu beantworten.

N3679: Async () zukünftige Destruktoren müssen warten

[..] Futures, die von async() mit async Start-Policy zurückgegeben werden, warten in ihrem Destruktor darauf, dass der zugehörige gemeinsame Status bereit ist. Dies verhindert eine Situation, in der der zugehörige Thread weiterhin ausgeführt wird, und es gibt keine Möglichkeit mehr, auf den Abschluss zu warten, da die zugehörige Zukunft zerstört wurde. Ohne heroische Anstrengungen, ansonsten auf die Fertigstellung zu warten, kann ein solcher “weglaufender” Thread die Lebensdauer der Objekte, von denen er abhängt, weiter ausführen.

[Beispiel]

Das Endergebnis ist wahrscheinlich ein Cross-Thread “Memory Smash”. Dieses Problem wird natürlich vermieden, wenn get() oder wait() [..] genannt wird, bevor sie [die Futures] zerstört werden. Die Schwierigkeit [..] besteht darin, dass eine unerwartete Ausnahme dazu führen kann, dass der Code umgangen wird. Daher wird normalerweise eine Art Schutzvorrichtung benötigt, um die Sicherheit zu gewährleisten. Wenn der Programmierer vergisst, den Schutz für den Schutzbereich hinzuzufügen, scheint es wahrscheinlich, dass ein Angreifer z. B. zu einem geeigneten Zeitpunkt eine “bad_alloc” -Ausnahme generieren kann, um den Überblick zu nutzen und einen Stapel zu überschreiben. Es kann möglich sein, auch die zum Überschreiben des Stapels verwendeten Daten zu steuern und somit die Kontrolle über den process zu erlangen. Dies ist ein ausreichend subtiler Fehler, der nach unserer Erfahrung im realen Code wahrscheinlich übersehen wird.

Update: Michael Wongs Reisebericht enthält auch einige interessante Informationen zu den Ergebnissen des Treffens im September 2013:

Der Blick vom C ++ Standard Meeting September 2013 Teil 2 von 2.

Zu dem Thema, dass asynchrone Destruktoren nicht blockieren sollten, haben wir viel darüber diskutiert. [..] Die einzige Position, die beträchtliche Unterstützung erhalten hat, war [..], Ratschläge zu geben, dass zukünftige Destruktoren nicht blockieren werden, wenn sie nicht von async zurückgegeben werden, was sie zur bemerkenswerten Ausnahme macht. [..] Nach bedeutender Diskussion war der einzige Teil, den wir zu tragen versuchten, N3776, ein Versuch, die Position zu klären, dass ~future und ~shared_future nicht blockieren, außer möglicherweise in der Anwesenheit von async. Es wurde versucht, eine Entwertung gemäß den Richtlinien von C. zu vercasting. Async ohne Ersatz zu vercasting. Dieser Antrag wurde eigentlich fast gestellt. Aber [..] es starb, noch bevor es den Operationstisch erreichte.