Namensräume
Varianten
Aktionen

std::async

Von cppreference.com
< cpp‎ | thread
 
 
Bibliothek für nebenläufige Programmierung
Threads
(C++11)
(C++20)
this_thread Namespace
(C++11)
(C++11)
(C++11)
Kooperatives Beenden
Gegenseitiger Ausschluss
(C++11)
Allgemeines Sperrungsmanagement
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Bedingungsvariablen
(C++11)
Semaphoren
Latches und Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
async
(C++11)
(C++11)
Sichere Wiederherstellung
(C++26)
Hazard Pointer
Atomare Typen
(C++11)
(C++20)
Initialisierung von atomaren Typen
(C++11)(veraltet in C++20)
(C++11)(veraltet in C++20)
Speicherordnung
(C++11)(deprecated in C++26)
Freie Funktionen für atomare Operationen
Freie Funktionen für atomare Flags
 
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,

                                    F&& f, Args&&... args );
(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.

1) Verhält sich so, als ob (2) mit policy als std::launch::async | std::launch::deferred aufgerufen wird.
2) Ruft eine Funktion f mit den Argumenten args gemäß einer spezifischen Startrichtlinie policy (siehe unten) auf.

Der Rückgabetyp von std::async ist std::future<V>, wobei V

typename std::result_of<typename std::decay<F>::type(
                        typename
std::decay<Args>::type...()>::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)),
       decay-copy(std::forward<Args>(args))...)

(bis C++23)

std::invoke(auto(std::forward<F>(f)),
            auto(std::forward<Args>(args))...)

(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::async dem Aufrufer zurückgegeben hat, blockiert, bis die asynchrone Operation abgeschlossen ist, wie durch join, 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ünglich std::async aufgerufen hat), wobei
(bis C++23)
  • g ist der gespeicherte Wert von auto(std::forward<F>(f)) und
  • xyz die gespeicherte Kopie von auto(std::forward<Args>(args))... ist.
(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

[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 der
Speicher für die internen Datenstrukturen nicht allokiert werden kann
wirft
LWG 3476 C++20 (die verfallenen Typen von) F und die Argumenttypen
mussten direkt Move-konstruierbar sein
entfernte diese Anforderungen[1]
  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) [bearbeiten]
C++ Dokumentation für Bibliothek für die Ausführungsunterstützung