Es hängt lose mit dieser Frage zusammen: Sind std :: thread in C ++ 11 zusammengefasst? . Obwohl die Frage unterschiedlich ist, ist die Absicht dieselbe:
Frage 1: Ist es immer noch sinnvoll, eigene Thread-Pools (oder Bibliotheken von Drittanbietern) zu verwenden, um eine teure Thread-Erstellung zu vermeiden?
Die Schlussfolgerung in der anderen Frage war, dass Sie sich nicht darauf verlassen können, zusammengefasst std::thread
zu werden (es könnte sein oder es könnte nicht sein). Allerdings std::async(launch::async)
scheint eine viel höhere Chance zu haben , gebündelt werden.
Es glaubt nicht, dass es vom Standard erzwungen wird, aber meiner Meinung nach würde ich erwarten, dass alle guten C ++ 11-Implementierungen Thread-Pooling verwenden, wenn die Thread-Erstellung langsam ist. Nur auf Plattformen, auf denen es kostengünstig ist, einen neuen Thread zu erstellen, würde ich erwarten, dass sie immer einen neuen Thread erzeugen.
Frage 2: Dies ist genau das, was ich denke, aber ich habe keine Fakten, um dies zu beweisen. Ich kann mich sehr gut irren. Ist es eine fundierte Vermutung?
Schließlich habe ich hier einen Beispielcode bereitgestellt, der zunächst zeigt, wie die Thread-Erstellung meiner Meinung nach ausgedrückt werden kann durch async(launch::async)
:
Beispiel 1:
thread t([]{ f(); });
// ...
t.join();
wird
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Beispiel 2: Faden feuern und vergessen
thread([]{ f(); }).detach();
wird
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Frage 3: Würden Sie die async
Versionen den thread
Versionen vorziehen ?
Der Rest ist nicht mehr Teil der Frage, sondern nur zur Klarstellung:
Warum muss der Rückgabewert einer Dummy-Variablen zugewiesen werden?
Leider erzwingt der aktuelle C ++ 11-Standard, dass Sie den Rückgabewert von erfassen std::async
, da andernfalls der Destruktor ausgeführt wird, der blockiert, bis die Aktion beendet wird. Es wird von einigen als Fehler in der Norm angesehen (z. B. von Herb Sutter).
Dieses Beispiel von cppreference.com veranschaulicht es gut:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Eine weitere Klarstellung:
Ich weiß, dass Thread-Pools andere legitime Verwendungszwecke haben können, aber in dieser Frage interessiert mich nur der Aspekt, teure Kosten für die Thread-Erstellung zu vermeiden .
Ich denke, es gibt immer noch Situationen, in denen Thread-Pools sehr nützlich sind, insbesondere wenn Sie mehr Kontrolle über Ressourcen benötigen. Beispielsweise kann ein Server entscheiden, nur eine feste Anzahl von Anforderungen gleichzeitig zu verarbeiten, um schnelle Antwortzeiten zu gewährleisten und die Vorhersagbarkeit der Speichernutzung zu erhöhen. Thread-Pools sollten hier in Ordnung sein.
Thread-lokale Variablen können auch ein Argument für Ihre eigenen Thread-Pools sein, aber ich bin mir nicht sicher, ob sie in der Praxis relevant sind:
- Erstellen eines neuen Threads mit
std::thread
Starts ohne initialisierte threadlokale Variablen. Vielleicht ist das nicht was du willst. - In Threads, die von erzeugt wurden
async
, ist es für mich etwas unklar, da der Thread möglicherweise wiederverwendet wurde. Nach meinem Verständnis wird nicht garantiert, dass threadlokale Variablen zurückgesetzt werden, aber ich kann mich irren. - Wenn Sie dagegen Ihre eigenen Thread-Pools (mit fester Größe) verwenden, haben Sie die volle Kontrolle, wenn Sie diese wirklich benötigen.
std::async()
. Ich bin immer noch gespannt, wie sie nicht triviale thread_local-Destruktoren in einem Thread-Pool unterstützen.
launch::async
ist, so behandelt wird, als ob sie nur wäre launch::deferred
und sie niemals asynchron ausführt. Tatsächlich wählt diese Version von libstdc ++ "aus". immer aufgeschoben zu verwenden, sofern nicht anders erzwungen.
std::async
könnte eine schöne Sache für die Leistung gewesen sein - es könnte das Standard-Ausführungssystem für kurzfristige Aufgaben gewesen sein, das natürlich von einem Thread-Pool unterstützt wird. Im Moment ist es nur ein std::thread
Mist, der angeheftet wird, damit die Thread-Funktion einen Wert zurückgeben kann. Oh, und sie haben redundante "verzögerte" Funktionen hinzugefügt, die den Job von std::function
vollständig überlappen .
std::async(launch::async)
scheint jedoch eine viel höhere Chance zu haben, gepoolt zu werden." Nein, ich glaubestd::async(launch::async | launch::deferred)
, das kann zusammengefasst werden. Mit nurlaunch::async
der Aufgabe soll auf einem neuen Thread gestartet werden, unabhängig davon, welche anderen Aufgaben ausgeführt werden. Mit der Richtlinie kannlaunch::async | launch::deferred
die Implementierung dann auswählen, welche Richtlinie, aber was noch wichtiger ist, sie kann die Auswahl der Richtlinie verzögern. Das heißt, es kann warten, bis ein Thread in einem Thread-Pool verfügbar wird, und dann die asynchrone Richtlinie auswählen.