Macht async (launch :: async) in C ++ 11 Thread-Pools überflüssig, um teure Thread-Erstellung zu vermeiden?

Es hängt lose mit dieser Frage zusammen:Sind std :: thread in C ++ 11 gepoolt?. Obwohl sich die Frage unterscheidet, ist die Absicht dieselbe:

Frage 1: Ist es immer noch sinnvoll, eigene Thread-Pools (oder Thread-Pools von Drittanbietern) zu verwenden, um teure Thread-Erstellung zu vermeiden?

Die Schlussfolgerung in der anderen Frage war, dass Sie sich nicht verlassen könnenstd::thread zusammengefasst werden (es könnte oder es könnte nicht sein). Jedoch,std::async(launch::async) scheint eine viel höhere Chance zu haben, gepoolt zu werden.

Ich glaube nicht, dass es durch den 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: Das ist genau das, was ich denke, aber ich habe keine Fakten, um es zu beweisen. Ich kann mich sehr gut irren. Ist es eine fundierte Vermutung?

Abschließend habe ich hier einen Beispielcode bereitgestellt, der zunächst zeigt, wie die Erstellung von Threads meiner Meinung nach ausgedrückt werden kannasync(launch::async):

Beispiel 1:

 thread t([]{ f(); });
 // ...
 t.join();

wird

 auto future = async(launch::async, []{ f(); });
 // ...
 future.wait();

Beispiel 2: Feuern und Thread 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 das vorziehen?async Versionen zumthread Versionen?

Der Rest ist nicht mehr Teil der Frage, sondern nur noch 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 erfassenstd::asyncAndernfalls wird der Destruktor ausgeführt, der blockiert, bis die Aktion beendet ist. Es wird von einigen als Fehler im Standard angesehen (z. B. von Herb Sutter).

Dieses Beispiel auscppreference.com illustriert es schön:

{
  std::async(std::launch::async, []{ f(); });
  std::async(std::launch::async, []{ g(); });  // does not run until f() completes
}

Eine weitere Klarstellung:

ich weiß dasThread-Pools können andere legitime Verwendungszwecke haben, aber in dieser Frage interessiert mich nur der Aspekt, teure Thread-Erstellungskosten 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 könnte ein Server beschließen, nur eine feste Anzahl von Anforderungen gleichzeitig zu verarbeiten, um schnelle Antwortzeiten zu gewährleisten und die Vorhersagbarkeit der Speichernutzung zu verbessern. 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 nicht sicher, ob es in der Praxis relevant ist:

Neuen Thread erstellen mitstd::thread startet ohne initialisierte threadlokale Variablen. Vielleicht ist das nicht was du willst.In Threads vonasyncist mir etwas unklar, da der thread hätte wiederverwendet werden können. Nach meinem Verständnis kann nicht garantiert werden, dass thread-lokale 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 sie wirklich benötigen.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage