Asynchron/wartet
Im Computerprogrammierung, das asynchron/wartet Muster ist ein syntaktisches Merkmal vieler Programmiersprachen, die eine erlaubt asynchron, nicht blockierend Funktion auf ähnliche Weise einer gewöhnlichen synchronen Funktion strukturiert werden. Es ist semantisch mit dem Konzept von a verwandt Coroutine und wird häufig mit ähnlichen Techniken implementiert und soll dem Programm in erster Linie die Möglichkeit bieten, einen anderen Code auszuführen und gleichzeitig auf eine langjährige, asynchrone Aufgabe zu warten, die normalerweise durch dargestellt wird durch Versprechen oder ähnliche Datenstrukturen. Die Funktion ist in gefunden in C# 5.0, C ++ 20, Python 3.5, F#, Hacken, Julia, Pfeil, Kotlin 1.1, Rost 1.39,[1] Nim 0.9.4,[2] JavaScript ES2017, Swift 5.5[3] und Zick,[4] mit einigen experimentellen Arbeiten in Erweiterungen, Beta -Versionen und bestimmten Implementierungen von Scala.[5]
Geschichte
F# fügte asynchrone Workflows mit Wartepunkten in Version 2.0 im Jahr 2007 hinzu.[6] Dies beeinflusste den asynchronisierten/warteten Mechanismus, der zu C#hinzugefügt wurde.[7]
Microsoft veröffentlichte zum ersten Mal eine Version von C# mit Async/wartet im Async CTP (2011). Und wurden später offiziell in C# 5 (2012) freigelassen.[8]
Haskell Lead Developer Simon Marlow Erstellt das Async -Paket im Jahr 2012.[9]
Python fügte Unterstützung für Async/Assait mit Version 3.5 im Jahr 2015 hinzu[10] Hinzufügen von 2 neuen Schlüsselwörtern, Async
und erwarten
.
TypeScript -Unterstützung für Async/Assait mit Version 1.7 im Jahr 2015.[11]
JavaScript fügte 2017 als Teil der JavaScript -Ausgabe von ECMAScript 2017 Unterstützung für Async/Awit 2017 hinzu.
Rost fügte Unterstützung für Async/Assait mit Version 1.39.0 im Jahr 2019 hinzu [12] mit 1 neues Schlüsselwort Async
Und ein fauler Eval -Awes -Atert -Muster.[13]
C ++ fügte Unterstützung für Async/Warten mit hinzu mit Version 20 im Jahr 2020 mit 3 neuen Schlüsselwörtern co_return
, Co_AWAIT
, co_yield
.
Swift fügte Unterstützung für Async/Wartee mit hinzu mit Version 5.5 Im Jahr 2021 fügen 2 neue Schlüsselwörter hinzu Async
und erwarten
. Dies wurde zusammen mit einer konkreten Implementierung der veröffentlicht Schauspielermodell mit dem Schauspieler
Stichwort[14] Dies nutzt Async/wartet, um den Zugang zu jedem Schauspieler von außen zu vermitteln.
Beispiel C#
Das C# Die Funktion unten, die eine Ressource von einem URI herunterlädt und die Länge der Ressource zurückgibt, verwendet dieses Async/Warte -Muster:
Öffentlichkeit Async Aufgabe<int> FindpageSizeasync(Uri Uri) { var Klient = Neu Httpclient(); Byte[] Daten = erwarten Klient.GetByTearrayasync(Uri); Rückkehr Daten.Länge; }
- Zuerst die
Async
Das Schlüsselwort zeigt C# an, dass die Methode asynchron ist, was bedeutet, dass sie eine willkürliche Anzahl von verwenden kannerwarten
Ausdrücke und wird das Ergebnis an a binden versprechen. - Der Rückgabetyp,
Aufgabe
, ist das Analogon von C#zum Konzept eines Versprechens, und hier ist angezeigt, dass er einen Ergebniswert des Typs hatint
. - Der erste Ausdruck, der ausgeführt wird, wenn diese Methode aufgerufen wird
new httpclient (). getByTearrayasync (URI)
, was eine weitere asynchrone Methode ist, die a zurückgibtAufgabe
. Da diese Methode asynchron ist, wird die gesamte Datenstapel vor der Rückkehr nicht heruntergeladen. Stattdessen beginnt der Downloadprozess mit einem nicht blockierenden Mechanismus (wie a Hintergrundfaden) und sofort eine ungelöste, ungeleinte zurückgebenAufgabe
zu dieser Funktion. - Mit dem
erwarten
Keyword an das beigefügtAufgabe
Diese Funktion wird sofort fortfahren, um a zurückzugebenAufgabe
an seinen Anrufer, der dann nach Bedarf mit einer anderen Verarbeitung fortfahren kann. - Einmal
GetByTearrayasync ()
beendet den Download, es wird die behebenAufgabe
Es wurde mit den heruntergeladenen Daten zurückgegeben. Dies wird einen Rückruf auslösen und verursachenFindpageSizeasync ()
Um die Ausführung fortzusetzen, indem Sie diesen Wert zuweisenDaten
. - Schließlich kehrt die Methode zurück
Daten.Length
, eine einfache Ganzzahl, die die Länge des Arrays angibt. Der Compiler interpretiert dies erneut als Lösung derAufgabe
Es wurde früher zurückgegeben und löste einen Rückruf im Anrufer der Methode aus, um etwas mit diesem Längenwert zu tun.
Eine Funktion mit Async/Awiit kann so viele verwenden erwarten
Ausdrücke, wie es will, und jeder wird auf die gleiche Weise behandelt (obwohl ein Versprechen nur zum ersten Warten an den Anrufer zurückgegeben wird, während jedes andere Warten interne Rückrufe verwendet). Eine Funktion kann auch ein Versprechensobjekt direkt aufnehmen und zuerst eine andere Verarbeitung durchführen (einschließlich anderer asynchroner Aufgaben), wodurch das Versprechen verzögert wird, bis ihr Ergebnis erforderlich ist. Funktionen mit Versprechen haben auch Versprechen Aggregationsmethoden, mit denen Sie mehrere Versprechungen gleichzeitig oder in einem speziellen Muster warten können (wie z. B. C# Task.whenall ()
, was eine wertlose zurückgibt Aufgabe
Das löst sich, wenn alle Aufgaben in den Argumenten gelöst sind). Viele Versprechungstypen haben auch zusätzliche Funktionen, die über das, was das Async/Warte-Muster normalerweise verwendet, übersehen, z. B. mehr als einen Ergebnisrückruf einrichten oder den Fortschritt einer besonders langlebigen Aufgabe überprüfen.
Im speziellen Fall von C#und in vielen anderen Sprachen mit dieser Sprachfunktion ist das Async/Ause -Muster kein zentraler Bestandteil der Laufzeit der Sprache, sondern wird stattdessen mit implementiert Lambdas oder Kontinuationen zur Kompilierungszeit. Zum Beispiel würde der C# Compiler den obigen Code wahrscheinlich in so etwas wie das Folgende übersetzen, bevor er ihn in seinen IL übersetzt Bytecode Format:
Öffentlichkeit Aufgabe<int> FindpageSizeasync(Uri Uri) { var Klient = Neu Httpclient(); Aufgabe<Byte[]> Datast = Klient.GetByTearrayasync(Uri); Aufgabe<int> AfterDatatask = Datast.Weitermachen mit((OriginalTask) => { Rückkehr OriginalTask.Ergebnis.Länge; }); Rückkehr AfterDatatask; }
Aus diesem Grund muss eine Schnittstellenmethode ein Versprechensobjekt zurückgeben, sich jedoch selbst nicht erfordert erwarten
im Körper, um auf asynchronen Aufgaben zu warten, braucht es das nicht Async
Modifikator und kann stattdessen ein Versprechensobjekt direkt zurückgeben. Beispielsweise kann eine Funktion möglicherweise ein Versprechen geben, das sofort einen Ergebniswert auflöst (z. B. C#'s Task.fromResult ()
) oder es kann einfach das Versprechen einer anderen Methode zurückgeben, das das genaue Versprechen ist (z. B. beim Aufschieben auf eine Überlastung).
Eine wichtige Einschränkung dieser Funktionalität ist jedoch, dass der Code zwar dem traditionellen Blockierungscode ähnelt, der Code jedoch tatsächlich nicht blockiert und möglicherweise multitHhreads ist, was bedeutet, dass viele intervenierende Ereignisse auftreten können, während sie auf das von einem abzielte Versprechen warten können erwarten
lösen. Zum Beispiel der folgende Code, während es immer in einem Blockierungsmodell ohne gelingt erwarten
, kann intervenieren Ereignisse während der erwarten
und kann also feststellen, dass der gemeinsame Zustand unter ihm ausgeschaltet wird:
var a = Zustand.a; var Klient = Neu Httpclient(); var Daten = erwarten Klient.GetByTearrayasync(Uri); Debuggen.Behaupten(a == Zustand.a); // potenzielles Versagen als Wert des Staates.a kann geändert worden sein // vom Handler des potenziell intervenierenden Ereignisses. Rückkehr Daten.Länge;
In f#
F# Asynchrone Workflows in Version 2.0 hinzugefügt.[15] Die asynchronen Workflows werden als CE implementiert (CE (Berechnungsausdrücke). Sie können definiert werden, ohne einen besonderen Kontext anzugeben (wie Async
in C#). F# Asynchrone Workflows fügen einen Bang (!) Anschlüsse hinzu, um asynchrone Aufgaben zu starten.
Die folgende Async -Funktion lädt Daten von einer URL unter Verwendung eines asynchronen Workflows herunter:
Lassen Asyncsumpages (URIS: #seq<Uri>)) : Async<int> = Async { verwenden httpclient = Neu Httpclient() Lassen! Seiten = URIS |> Seq.Karte(httpclient.Getstringasync >> Async.Wartetask) |> Async.Parallel Rückkehr Seiten |> Seq.falten (Spaß Akkumulator aktuell -> aktuell.Länge + Akkumulator) 0 }
In C#
Das Async/Ause-Muster in C# ist ab Version 5.0 verfügbar, das Microsoft als aufgabenbasierte asynchrones Muster (TAP) bezeichnet.[16] Asynchronisierte Methoden sind erforderlich, um entweder zurückzukehren Leere
, Aufgabe
, Aufgabe
, oder Wertschöpfung
(Letzteres nur als Version 7.0). Asynchrische Methoden, die zurückkehren Leere
sind für Event -Handler; In den meisten Fällen, in denen eine synchrone Methode zurückkehren würde Leere
, zurückkehren Aufgabe
stattdessen wird empfohlen, da es eine intuitivere Ausnahmebehandlung ermöglicht.[17]
Methoden, die verwendet werden von erwarten
muss mit dem deklariert werden Async
Stichwort. In Methoden mit einem Rückgabewert des Typs Aufgabe
, Methoden mit deklariert mit Async
Muss eine Rückgabeanweisung vom Typ haben, der zugewiesen werden kann T
Anstatt von Aufgabe
; Der Compiler wickelt den Wert in die Aufgabe
generisch. Es ist auch möglich erwarten
Methoden, die einen Rückgabetyp haben Aufgabe
oder Aufgabe
das werden ohne deklariert Async
.
Die folgende Async -Methode lädt Daten von einer URL aus erwarten
. Weil diese Methode eine Aufgabe für jeden URI ausgibt, bevor er mit dem fertiggestellt wird erwarten
Schlüsselwort, die Ressourcen können gleichzeitig geladen werden, anstatt darauf zu warten, dass die letzte Ressource fertig ist, bevor Sie mit dem Laden beginnen.
Öffentlichkeit Async Aufgabe<int> SumpageSizeSasync(ICollection<Uri> URIS) { var Klient = Neu Httpclient(); int gesamt = 0; var Loaduritasks = Neu Aufführen<Aufgabe<Byte[] >> (); für jeden (var Uri in URIS) { var Loaduritask = Klient.GetByTearrayasync(Uri); Loaduritasks.Hinzufügen(Loaduritask ); } für jeden (var Loaduritask in Loaduritasks) { StatuStext.Text = $ "Found {Total} Bytes ..."; var ResourceAsBytes = erwarten Loaduritask; gesamt += ResourceAsBytes.Länge; } StatuStext.Text = $ "Found {Total} Bytes Total"; Rückkehr gesamt; }
In Scala
In der experimentellen scala-asynchronisierten Erweiterung auf Scala,, await
ist eine "Methode", obwohl sie nicht wie eine gewöhnliche Methode funktioniert.[5] Darüber hinaus im Gegensatz zu C# 5.0, bei dem eine Methode als asynchronisiert werden muss, in scala-async, a Block Code ist von einem asynchronen "Anruf" umgeben.
Wie es funktioniert
In scalaa-async, async
wird tatsächlich mit einem Scala -Makro implementiert, das das verursacht Compiler verschiedene Code auszugeben und a zu produzieren endliche Zustandsmaschine Implementierung (die als effizienter angesehen wird als a monadisch Implementierung, aber weniger bequem von Hand zu schreiben).
Es gibt Pläne für Scala-async, eine Vielzahl verschiedener Implementierungen zu unterstützen, einschließlich nicht-asynchroner.
In Python
Python 3.5 (2015)[18] hat Unterstützung für asynchronisiert/wartet wie in PEP 492 beschrieben (beschrieben)https://www.python.org/dev/peps/pep-0492/).
importieren Asyncio Async def hauptsächlich(): drucken("hallo") erwarten Asyncio.schlafen(1) drucken("Welt") Asyncio.Lauf(hauptsächlich())
In JavaScript
Der erwartete Operator in JavaScript kann nur in einer asynchronisierenden Funktion verwendet werden. Wenn der Parameter a ist versprechen, Ausführung der asynchronisierten Funktion wird wieder aufgenommen, wenn das Versprechen gelöst wird (es sei denn, das Versprechen wird abgelehnt. In diesem Fall wird ein Fehler geworfen, der mit normalem JavaScript behandelt werden kann Ausnahmebehandlung). Wenn der Parameter kein Versprechen ist, wird der Parameter selbst sofort zurückgegeben.[19]
Viele Bibliotheken bieten vielversprechende Objekte, die auch mit Warten verwendet werden können, solange sie der Spezifikation für native JavaScript -Versprechen entsprechen. Versprechungen aus dem jedoch JQuery Die Bibliothek war bis JQuery 3.0 keine Versprechen/A+ kompatibel.[20]
Hier ist ein Beispiel (daraus geändert[21] Artikel):
Async Funktion CreateNewdoc() { Lassen Antwort = erwarten db.Post({}); // einen neuen Dokument veröffentlichen Rückkehr db.erhalten(Antwort.Ich würde); // durch ID finden } Async Funktion hauptsächlich() { Versuchen { Lassen Dokument = erwarten CreateNewdoc(); Konsole.Protokoll(Dokument); } Fang (irren) { Konsole.Protokoll(irren); } } hauptsächlich();
Node.js Version 8 enthält ein Dienstprogramm, das die Verwendung der Standard-Callback-Methoden der Standardbibliothek als Versprechen ermöglicht.[22]
In C ++
In C ++ wurde erwarten (mit dem Namen Co_AWAIT in C ++) offiziell zusammengeführt Version 20.[23] Unterstützung dafür, Coroutinen und die Schlüsselwörter wie Co_AWAIT
ist erhältlich in GCC und MSVC Compiler während Klang hat teilweise Unterstützung.
Es ist erwähnenswert, dass STD :: Promise und Std :: Future, obwohl es anscheinend erwartbare Objekte wären, keine der Maschinen implementieren, die aus Coroutinen zurückgegeben und mit Co_AWAIT erwartet werden müssen. Programmierer müssen eine Reihe von öffentlichen Mitgliedsfunktionen implementieren, wie z. warte_ready
, Warten_Suspend
, und warte_resume
Auf dem Rückgabetyp, damit der Typ erwartet wird. Details finden Sie auf cppreferenz.
#enthalten #enthalten "CustomAwaitabletask.h" Verwendung Namespace std; CustomAwaitabletask<int> hinzufügen(int a, int b) { int c = a + b; co_return c; } CustomAwaitabletask<int> Prüfung() { int ret = Co_AWAIT hinzufügen(1, 2); Cout << "Rückkehr " << ret << Endl; co_return ret; } int hauptsächlich() { Auto Aufgabe = Prüfung(); Rückkehr 0; }
In c
In der C -Sprache gibt es noch keine offizielle Unterstützung für Warte-/Asynchronisierung. Einige Coroutine -Bibliotheken wie s_task Simulieren Sie die Schlüsselwörter warten/asynchron mit Makros.
#enthalten #enthalten "s_task.h" // Stapelspeicher für Aufgaben definieren int g_stack_main[64 * 1024 / Größe von(int)]; int g_stack0[64 * 1024 / Größe von(int)]; int g_stack1[64 * 1024 / Größe von(int)]; Leere sub_task(__async__, Leere* arg) { int i; int n = (int) (size_t)arg; zum (i = 0; i < 5; ++i) { printf("Task %d, verzögerte Sekunden = %d, i = %d\n", n, n, i); s_task_msleep(__erwarten__, n * 1000); // s_task_yield (__ warte__); } } Leere Hauptaufgabe(__async__, Leere* arg) { int i; // Erstellen Sie zwei Unterbereitungen s_task_create(g_stack0, Größe von(g_stack0), sub_task, (Leere*)1); s_task_create(g_stack1, Größe von(g_stack1), sub_task, (Leere*)2); zum (i = 0; i < 4; ++i) { printf("Task_main arg = %p, i = %d\n", arg, i); s_task_yield(__erwarten__); } // Warten Sie auf die Unterbereitungen zum Ausgang s_task_join(__erwarten__, g_stack0); s_task_join(__erwarten__, g_stack1); } int hauptsächlich(int argc, verkohlen* argv) { s_task_init_system(); // Erstellen Sie die Hauptaufgabe s_task_create(g_stack_main, Größe von(g_stack_main), Hauptaufgabe, (Leere*) (size_t)argc); s_task_join(__erwarten__, g_stack_main); printf("Alle Aufgabe ist vorbei\n"); Rückkehr 0; }
In Perl 5
Das Zukunft :: Asyncawait Das Modul war im September 2018 Gegenstand eines Zuschusss der Perl Foundation.[24]
Im Rost
Am 7. November 2019 wurde Async/Awit in der stabilen Version von Rust veröffentlicht.[25] Asynchronistische Funktionen in Rost Desugar zu einfachen Funktionen, die Werte zurückgeben, die das zukünftige Merkmal implementieren. Derzeit werden sie mit a implementiert endliche Zustandsmaschine.[26]
// In der Fracht der Kiste benötigen wir im Abschnitt Abhängigkeiten `futures =" 0.3.0 ". // damit wir die Futures -Kiste verwenden können extern Kiste Futures; // Es gibt derzeit keinen Testamentsvollstrecker in der "std" -Bibliothek. // diese Deckars zu so etwas wie // `fn async_add_one (num: u32) -> impl zukünftig <output = u32>` Async fn async_add_one(num: U32) -> U32 { num + 1 } Async fn Beispiel_task() { Lassen Nummer = async_add_one(5).erwarten; println!("5 + 1 = {}", Nummer); } fn hauptsächlich() { // Erstellen der Zukunft startet nicht die Ausführung. Lassen Zukunft = Beispiel_task(); // Die "Future" wird im Gegensatz zu JavaScript nur ausgeführt, wenn wir es tatsächlich befragen. Futures::Testamentsvollstrecker::Block_on(Zukunft); }
In Swift
Swift 5.5 (2021)[27] Unterstützung für Async/Awit, wie in SE-0296 beschrieben (beschrieben)https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md).
Func Getnumber() Async Würfe -> Int { Versuchen erwarten Aufgabe.schlafen(Nanosekunden: 1_000_000_000) Rückkehr 42 } Aufgabe { Lassen Erste = Versuchen erwarten Getnumber() Lassen zweite = Versuchen erwarten Getnumber() drucken(Erste + zweite) }
Vorteile und Kritik
Ein wesentlicher Vorteil des asynchronisierten/wartenden Musters in Sprachen, die es unterstützen, ist, dass asynchroner, nicht blockierender Code mit minimalem Overhead geschrieben werden kann und fast wie herkömmliche synchrone, blockierende Code aussieht. Insbesondere wurde argumentiert, dass das Warten der beste Weg ist, einen asynchronen Code in zu schreiben Nachrichten-Passing Programme; Insbesondere blockierende Code, Lesbarkeit und die minimale Menge von blockierend sein Boilerplate -Code wurden als Wartezeit von Vorteilen zitiert.[28] Infolgedessen erleichtert Async/Assait es den meisten Programmierern einfacher, über ihre Programme zu argumentieren, und erwarten Sie dazu, dass in Anwendungen, die dies erfordern, einen besseren, robusteren, robusteren Code für nicht blockierende Code fördern. Solche Anwendungen reichen von Programmen, die präsentiert werden Grafische Benutzeroberflächen an massiv skalierbare staatlich serverseitige Programme wie Spiele und finanzielle Anwendungen.
Bei der Kritik erwartet wurde angemerkt, dass wart es auch dazu, dass der Umgebungscode auch asynchron ist. Andererseits wurde argumentiert, dass diese ansteckende Natur des Codes (manchmal mit einem "Zombie -Virus") allen Arten von asynchronem Programmieren innewohnt, also wart es in dieser Hinsicht nicht einzigartig.[17]
Siehe auch
Verweise
- ^ "Ankündigung von Rost 1.39.0". Abgerufen 2019-11-07.
- ^ "Version 0.9.4 Veröffentlicht - Nim Blog". Abgerufen 2020-01-19.
- ^ "Parallelität - Die Swift -Programmiersprache (Swift 5.5)". docs.swift.org. Abgerufen 2021-09-28.
- ^ "Zick -Sprachreferenz".
- ^ a b "Scala Async". GitHub. Abgerufen 20. Oktober 2013.
- ^ Syme, Don; Petricek, Tomas; Lomov, Dmitry (2011). Das F# Asynchronische Programmiermodell. Springer Link. Vorlesungsnotizen in Informatik. Vol. 6539. S. 175–189. doi:10.1007/978-3-642-18378-2_15. ISBN 978-3-642-18377-5. Abgerufen 2021-04-29.
- ^ "Die frühe Geschichte von F#, Hopl IV". ACM Digitale Bibliothek. Abgerufen 2021-04-29.
- ^ Hejlsberg, Anders. "Anders Hejlsberg: Einführung von Async - Vereinfachung der asynchrone Programmierung". Kanal 9 msdn. Microsoft. Abgerufen 5. Januar 2021.
- ^ "Async: Führen Sie IO -Operationen asynchron aus und warten Sie auf ihre Ergebnisse.". Hackage.
- ^ "Was ist neu in Python 3.5 - Python 3.9.1 Dokumentation". docs.python.org. Abgerufen 5. Januar 2021.
- ^ Gaurav, Seth (30. November 2015). "Ankündigung von TypeScript 1.7". Typoskript. Microsoft. Abgerufen 5. Januar 2021.
- ^ Matsakis, Niko. "Async-erwartet auf stabile Rost! | Rust Blog". blog.rustlang.org. Rust Blog. Abgerufen 5. Januar 2021.
- ^ "Rust bekommt keine Asynchronisation ohne Cost/wartet in Rost 1.39.".
- ^ "Parallelität - Die Swift -Programmiersprache (Swift 5.6)".
- ^ "Einführung von F# Asynchronen Workflows".
- ^ "Aufgabenbasierter asynchrones Muster". Microsoft. Abgerufen 28. September 2020.
- ^ a b Stephen Cleary, Asynchron/wartet - Best Practices in der asynchronen Programmierung
- ^ "Python Release Python 3.5.0".
- ^ "Warte - JavaScript (MDN)". Abgerufen 2. Mai 2017.
- ^ "JQuery Core 3.0 Upgrade Guide". Abgerufen 2. Mai 2017.
- ^ "Das asynchrone Tier mit ES7 zähle". Abgerufen 12. November 2015.
- ^ Foundation, Node.js. "Knoten v8.0.0 (Strom) - node.js". Node.js.
- ^ "ISO C ++ - Komitee kündigt an, dass C ++ 20 Design jetzt komplett ist". 25. Februar 2019.
- ^ "September 2018 Grant Votes - The Perl Foundation". news.perlfoundation.org. Abgerufen 2019-03-26.
- ^ Matsakis, Niko. "Async-erwartet auf stabilen Rost!". Rust Blog. Abgerufen 7. November 2019.
- ^ Oppermann, Philipp. "Asynchron/wartet". Abgerufen 28. Oktober 2020.
- ^ https://www.swift.org/blog/swift-5-5-releierte/
- ^ "No Bugs" Hase. Acht Möglichkeiten, um nicht blockierende Renditen in Message-Passing-Programmen umzugehen CPPCON, 2018