std::async
| Definiert im Header <future> |
||
| template< class F, class... Args > std::future</* siehe unten */> async( F&& f, Args&&... args ); |
(1) | (seit C++11) |
| template< class F, class... Args > std::future</* siehe unten */> async( std::launch policy, |
(2) | (seit C++11) |
Die Funktion Template std::async führt die Funktion f asynchron aus (möglicherweise in einem separaten Thread, der Teil eines Thread-Pools sein kann) und gibt ein std::future zurück, das schließlich das Ergebnis dieses Funktionsaufrufs enthalten wird.
Der Rückgabetyp von std::async ist std::future<V>, wobei V
|
typename std::result_of<typename std::decay<F>::type( |
(bis C++17) |
|
std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>. |
(seit C++17) |
|
Wenn eine der folgenden Bedingungen erfüllt ist, ist das Programm ill-formed (wohlgeformt):
|
(bis C++20) |
|
Wenn einer der folgenden Ausdrücke false ist, ist das Programm ill-formed.
|
(seit C++20) |
Der Aufruf von std::async synchronisiert mit dem Aufruf von f, und die Fertigstellung von f ist sequenziert vor dem Bereitmachen des gemeinsamen Zustands.
Inhalt |
[edit] Parameter
| f | - | Callable-Objekt, das aufgerufen werden soll |
| args | - | Parameter, die an f übergeben werden sollen |
| policy | - | Bitmaskenwert, wobei einzelne Bits die erlaubten Ausführungsmethoden steuern |
[edit] Rückgabewert
std::future, der auf den durch diesen Aufruf von std::async erstellten gemeinsamen Zustand verweist.
[edit] Startrichtlinien
[edit] Async-Aufruf
Wenn das async-Flag gesetzt ist, d.h. (policy & std::launch::async) != 0, dann ruft std::async
|
INVOKE(decay-copy(std::forward<F>(f)), |
(bis C++23) |
|
std::invoke(auto(std::forward<F>(f)), |
(seit C++23) |
wie in einem neuen Ausführungsthread, der durch ein std::thread-Objekt repräsentiert wird.
|
Die Aufrufe von decay-copy werden im aktuellen Thread ausgewertet. |
(bis C++23) |
|
Die von auto produzierten Werte werden im aktuellen Thread materialisiert. |
(seit C++23) |
Wenn die Funktion f einen Wert zurückgibt oder eine Ausnahme wirft, wird sie im gemeinsamen Zustand gespeichert, der über den std::future zugänglich ist, den std::async dem Aufrufer zurückgibt.
[edit] Deferred-Aufruf
Wenn das deferred-Flag gesetzt ist (d.h. (policy & std::launch::deferred) != 0), speichert std::async
|
decay-copy(std::forward<F>(f)) und decay-copy(std::forward<Args>(args))... im gemeinsamen Zustand. |
(bis C++23) |
|
auto(std::forward<F>(f)) und auto(std::forward<Args>(args))... im gemeinsamen Zustand. |
(seit C++23) |
Lazy Evaluation wird durchgeführt.
- Der erste Aufruf einer nicht-zeitgebundenen Wartefunktion für das std::future, das
std::asyncdem Aufrufer zurückgegeben hat, blockiert, bis die asynchrone Operation abgeschlossen ist, wie durchjoin, oder bis zum Timeout. dabei wird INVOKE(std::move(g), std::move(xyz)) in dem Thread ausgeführt, der die Wartefunktion aufgerufen hat (was nicht unbedingt der Thread sein muss, der ursprünglichstd::asyncaufgerufen hat), wobei
|
(bis C++23) |
|
(seit C++23) |
- Das Ergebnis oder die Ausnahme wird in den gemeinsamen Zustand gelegt, der mit dem zurückgegebenen std::future verbunden ist, und erst dann wird dieser als bereit markiert. Alle weiteren Zugriffe auf dasselbe std::future geben das Ergebnis sofort zurück.
[edit] Andere Richtlinien
Wenn weder std::launch::async noch std::launch::deferred noch eine implementierungsdefinierte Richtlinienflag in policy gesetzt ist, ist das Verhalten undefiniert.
[edit] Auswahl der Richtlinie
Wenn mehr als ein Flag gesetzt ist, ist es implementierungsdefiniert, welche Richtlinie ausgewählt wird. Für die Standardrichtlinie (bei der sowohl std::launch::async als auch std::launch::deferred in policy gesetzt sind) empfiehlt der Standard (ohne dies zu verlangen), die verfügbare Nebenläufigkeit zu nutzen und zusätzliche Aufgaben aufzuschieben.
Wenn die std::launch::async-Richtlinie gewählt wird,
- blockiert ein Aufruf einer Wartefunktion auf einem asynchronen Rückgabeobjekt, das denselben gemeinsamen Zustand wie dieser
std::async-Aufruf teilt, bis der zugehörige Thread abgeschlossen ist (als ob er beigetreten wäre) oder bis zu einem Timeout; und - die Fertigstellung des zugehörigen Threads *synchronisiert mit* dem erfolgreichen Rücksprung aus der ersten Funktion, die auf den gemeinsamen Zustand wartet, oder mit dem Rücksprung der letzten Funktion, die den gemeinsamen Zustand freigibt, je nachdem, was zuerst eintritt.
[edit] Ausnahmen
Wirft
- std::bad_alloc, wenn der Speicher für die internen Datenstrukturen nicht allokiert werden kann, oder
- std::system_error mit dem Fehlerzustand std::errc::resource_unavailable_try_again, wenn policy == std::launch::async und die Implementierung keinen neuen Thread starten kann.
- Wenn policy std::launch::async | std::launch::deferred ist oder zusätzliche Bits gesetzt hat, wird in diesem Fall auf Deferred-Aufruf oder implementierungsdefinierte Richtlinien zurückgegriffen.
[edit] Hinweise
Die Implementierung kann das Verhalten der ersten Überladung von std::async erweitern, indem sie zusätzliche (implementierungsdefinierte) Bits in der Standard-Startrichtlinie aktiviert.
Beispiele für implementierungsdefinierte Startrichtlinien sind die Sync-Richtlinie (sofortige Ausführung innerhalb des std::async-Aufrufs) und die Task-Richtlinie (ähnlich wie std::async, aber Thread-Locals werden nicht gelöscht).
Wenn das von std::async erhaltene std::future nicht verschoben oder an eine Referenz gebunden wird, blockiert der Destruktor des std::future am Ende des vollständigen Ausdrucks, bis die asynchrone Operation abgeschlossen ist, was den folgenden Code im Wesentlichen synchronisiert:
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f() std::async(std::launch::async, []{ g(); }); // does not start until f() completes
Beachten Sie, dass die Destruktoren von std::futures, die auf andere Weise als durch einen Aufruf von std::async erhalten wurden, niemals blockieren.
[edit] Beispiel
#include <algorithm> #include <future> #include <iostream> #include <mutex> #include <numeric> #include <string> #include <vector> std::mutex m; struct X { void foo(int i, const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << ' ' << i << '\n'; } void bar(const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << '\n'; } int operator()(int i) { std::lock_guard<std::mutex> lk(m); std::cout << i << '\n'; return i + 10; } }; template<typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len / 2; auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // Calls (&x)->foo(42, "Hello") with default policy: // may print "Hello 42" concurrently or defer execution auto a1 = std::async(&X::foo, &x, 42, "Hello"); // Calls x.bar("world!") with deferred policy // prints "world!" when a2.get() or a2.wait() is called auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // Calls X()(43); with async policy // prints "43" concurrently auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // prints "world!" std::cout << a3.get() << '\n'; // prints "53" } // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
Mögliche Ausgabe
The sum is 10000 43 world! 53 Hello 42
[edit] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| LWG 2021 | C++11 | Rückgabetyp und Wertkategorie von Argumenten im Deferred-Fall unklar |
korrigierter Rückgabetyp und klarstellung, dass Rvalues verwendet werden |
| LWG 2078 | C++11 | war unklar, ob std::system_error geworfen werden kann, wenn policy andere Startrichtlinien außer std::launch::async spezifiziert |
kann nur geworfen werden, wenn policy == std::launch::async |
| LWG 2100 | C++11 | zeitgebundene Wartefunktionen konnten nicht abbrechen wenn std::launch::async-Richtlinie verwendet wird |
erlaubt |
| LWG 2120 | C++11 | das Verhalten war unklar, wenn keine Standard- oder implementierungsdefinierte Richtlinie gesetzt ist |
Das Verhalten ist in diesem Fall nicht definiert. |
| LWG 2186 | C++11 | war unklar, wie der zurückgegebene Wert und die Ausnahme aus der Lazy Evaluation behandelt werden |
sie werden im gemeinsamen Zustand gespeichert |
| LWG 2752 | C++11 | std::async wirft möglicherweise nicht std::bad_alloc, wenn derSpeicher für die internen Datenstrukturen nicht allokiert werden kann |
wirft |
| LWG 3476 | C++20 | (die verfallenen Typen von) F und die Argumenttypenmussten direkt Move-konstruierbar sein |
entfernte diese Anforderungen[1] |
- ↑ Die Move-Konstruierbarkeit ist bereits indirekt durch std::is_constructible_v erforderlich.
[edit] Siehe auch
| (C++11) |
wartet auf einen Wert, der asynchron gesetzt wird (Klassenvorlage) |
| C++ Dokumentation für Bibliothek für die Ausführungsunterstützung
| |