it-swarm.com.de

Warum sollte ich std :: async verwenden?

Ich versuche, alle Optionen des neuen C++ 11-Standards eingehend zu erkunden. Beim Verwenden von std :: async und beim Lesen der Definition sind mir zwei Dinge aufgefallen, zumindest unter Linux mit gcc 4.8.1:

  • es heißt async, hat jedoch ein wirklich "sequentielles Verhalten", im Wesentlichen in der Zeile, in der Sie das future aufrufen, das Ihrer asynchronen Funktion foo zugeordnet ist, dem Programm blockiert, bis die Ausführung von foo abgeschlossen ist. 
  • es hängt von der gleichen externen Bibliothek wie von anderen ab, und von besseren, nicht blockierenden Lösungen, was pthread bedeutet, wenn Sie std::async verwenden möchten, benötigen Sie pthread.

an diesem Punkt ist es natürlich für mich zu fragen, warum man std :: async sogar über einen einfachen Satz von Funktoren entscheidet? Es ist eine Lösung, die nicht einmal skalierbar ist. Je mehr Sie anrufen, desto weniger ansprechbar ist Ihr Programm.

Fehlt mir etwas? Können Sie ein Beispiel zeigen, das asynchron, nicht blockierend ausgeführt werden kann?

54
user2485710

Wenn Sie das Ergebnis einer asynchronen Operation benötigen, müssen Sie haben blockieren, unabhängig davon, welche Bibliothek Sie verwenden. Die Idee ist, dass Sie entscheiden können, wann Sie blockieren möchten, und wenn Sie dies tun, werden Sie hoffentlich für eine vernachlässigbare Zeit blockiert, da die gesamte Arbeit bereits erledigt ist. 

Beachten Sie auch, dass std::async mit den Richtlinien std::launch::async oder std::launch::deferred gestartet werden kann. Wenn Sie sie nicht angeben, kann die Implementierung wählen, und es kann durchaus eine verzögerte Auswertung verwendet werden, was dazu führen würde, dass alle Arbeiten erledigt werden, wenn Sie versuchen, das Ergebnis aus der Zukunft zu erhalten, was zu einer längeren Blockierung führt . Wenn Sie also sicherstellen möchten, dass die Arbeit asynchron ausgeführt wird, verwenden Sie std::launch::async.

53
juanchopanza
  • es heißt async, aber es hat ein wirklich "sequentielles Verhalten" bekommen,

Nein, wenn Sie die std::launch::async-Richtlinie verwenden, wird sie asynchron in einem neuen Thread ausgeführt. Wenn Sie keine Richtlinie angeben, wird möglicherweise in einem neuen Thread ausgeführt.

im Wesentlichen in der Zeile, in der Sie die mit Ihrer asynchronen Funktion foo verknüpfte Zukunft aufrufen, blockiert das Programm, bis die Ausführung von foo abgeschlossen ist.

Es blockiert nur, wenn foo noch nicht abgeschlossen ist. Wenn es jedoch asynchron ausgeführt wurde (z. B. weil Sie die std::launch::async-Richtlinie verwenden), wurde es möglicherweise abgeschlossen, bevor Sie es benötigen.

  • es hängt von der gleichen externen Bibliothek wie von anderen ab, und von besseren, nicht blockierenden Lösungen, d. h. pthread. Wenn Sie std :: async verwenden möchten, benötigen Sie pthread.

Falsch, es muss nicht mit Pthreads implementiert werden (und unter Windows ist dies nicht der Fall, es verwendet die ConcRT-Funktionen.)

an diesem Punkt ist es natürlich für mich zu fragen, warum man std :: async sogar über einen einfachen Satz von Funktoren entscheidet?

Weil es Thread-Sicherheit garantiert und Ausnahmen über Threads verbreitet. Können Sie das mit einem einfachen Satz von Funktoren tun?

Es ist eine Lösung, die nicht einmal skalierbar ist. Je mehr Sie anrufen, desto weniger ansprechbar ist Ihr Programm.

Nicht unbedingt. Wenn Sie keine Startrichtlinie angeben, kann eine intelligente Implementierung entscheiden, ob Sie einen neuen Thread starten oder eine zurückgestellte Funktion zurückgeben oder etwas zurückgeben möchten, das später entscheidet, wenn möglicherweise mehr Ressourcen verfügbar sind.

Es stimmt, dass mit der Implementierung von GCC, wenn Sie keine Startrichtlinie angeben, diese mit aktuellen Releases niemals in einem neuen Thread ausgeführt wird (es gibt einen bugzilla-Bericht dafür). Dies ist jedoch eine Eigenschaft dieser Implementierung. im Allgemeinen nicht von std::async. Sie sollten die Spezifikation im Standard nicht mit einer bestimmten Implementierung verwechseln. Das Lesen der Implementierung einer Standardbibliothek ist ein schlechter Weg, um C++ 11 zu lernen. 

Können Sie ein Beispiel zeigen, das asynchron, nicht blockierend ausgeführt werden kann?

Dies sollte nicht blockieren:

auto fut = std::async(std::launch::async, doSomethingThatTakesTenSeconds);
auto result1 = doSomethingThatTakesTwentySeconds();
auto result2 = fut.get();

Durch die Angabe der Startrichtlinie erzwingen Sie die asynchrone Ausführung. Wenn Sie während der Ausführung andere Arbeiten ausführen, ist das Ergebnis sofort verfügbar, wenn Sie es benötigen.

71
Jonathan Wakely

Ich denke, Ihr Problem liegt darin, dass std::future sagt, dass es auf get blockiert. Es blockiert nur wenn das Ergebnis noch nicht fertig ist .

Wenn Sie das Ergebnis bereits fertig stellen können, ist dies kein Problem.

Es gibt viele Möglichkeiten, um zu wissen, dass das Ergebnis bereits fertig ist. Sie können future abfragen und danach fragen (relativ einfach). Sie können Sperren oder atomare Daten verwenden, um die Tatsache weiterzuleiten, dass sie bereit ist. Sie können ein Framework aufbauen, um "beendete" future-Elemente in eine Warteschlange zu stellen, mit der Benutzer interagieren können , Sie könnten Signale irgendeiner Art verwenden (die einfach mehrere Dinge gleichzeitig blockieren oder abfragen).

Oder Sie können alle Arbeiten beenden, die Sie lokal ausführen können, und dann die Remote-Arbeit blockieren.

Stellen Sie sich als Beispiel eine parallele rekursive Zusammenführungssorte vor. Es teilt das Array in zwei Blöcke auf und führt dann eine Sortierung async für einen Block aus, während der andere Block sortiert wird. Sobald die Hälfte sortiert ist, kann der Ursprungs-Thread nicht fortfahren, bis die zweite Aufgabe abgeschlossen ist. Also macht es eine .get() und blockiert. Nachdem beide Hälften sortiert wurden, kann eine Zusammenführung vorgenommen werden (theoretisch kann die Zusammenführung auch zumindest teilweise parallel erfolgen).

Diese Task verhält sich wie eine lineare Task für diejenigen, die außen mit ihr interagieren - wenn das erledigt ist, wird das Array sortiert.

Wir können dies dann in eine std::async-Task einschließen und ein future sortiertes Array haben. Wenn wir möchten, können wir in einer Signalprozedur hinzufügen, dass die Variable future fertig ist. Dies macht jedoch nur dann Sinn, wenn ein Thread auf die Signale wartet.

In der Referenz : http://en.cppreference.com/w/cpp/thread/async

Wenn das async-Flag gesetzt ist (d. H. Policy & std :: launch :: async! = 0), dann async führt die Funktion f in einem separaten Ausführungsthread aus, als ob erzeugt durch std :: thread (f, args ...), außer dass wenn die Funktion f Gibt einen Wert zurück oder löst eine Ausnahme aus, es wird in der gemeinsam genutzten .__ gespeichert. Zustand, auf den über std :: future zugegriffen werden kann, wenn async zur .__ Anrufer.

Es ist eine schöne Eigenschaft von Nizza, um die Anzahl der ausgelösten Ausnahmen festzuhalten.

3
fatihk

http://www.cplusplus.com/reference/future/async/

es gibt drei Arten von Richtlinien,

  1. launch::async
  2. launch::deferred
  3. launch::async|launch::deferred

standardmäßig wird launch::async|launch::deferred an std::async übergeben.

0