Eine Coroutine ist eine Generatorfunktion, die sowohl Werte liefern als auch Werte von außen akzeptieren kann. Der Vorteil der Verwendung einer Coroutine besteht darin, dass wir die Ausführung einer Funktion anhalten und später fortsetzen können. Im Falle eines Netzwerkbetriebs ist es sinnvoll, die Ausführung einer Funktion anzuhalten, während wir auf die Antwort warten. Wir können die Zeit nutzen, um einige andere Funktionen auszuführen.
Eine Zukunft ist wie die Promise
Objekte aus Javascript. Es ist wie ein Platzhalter für einen Wert, der in Zukunft materialisiert wird. In dem oben genannten Fall kann uns eine Funktion während des Wartens auf Netzwerk-E / A einen Container geben, ein Versprechen, dass der Container nach Abschluss des Vorgangs mit dem Wert gefüllt wird. Wir halten am zukünftigen Objekt fest und wenn es erfüllt ist, können wir eine Methode aufrufen, um das tatsächliche Ergebnis abzurufen.
Direkte Antwort: Sie brauchen nicht, ensure_future
wenn Sie die Ergebnisse nicht brauchen. Sie sind gut, wenn Sie die Ergebnisse benötigen oder Ausnahmen abrufen.
Zusätzliche Credits: Ich würde run_in_executor
eine Executor
Instanz auswählen und übergeben , um die Anzahl der maximalen Mitarbeiter zu steuern.
Erklärungen und Beispielcodes
Im ersten Beispiel verwenden Sie Coroutinen. Die wait
Funktion nimmt eine Reihe von Coroutinen und kombiniert sie miteinander. Wird wait()
beendet, wenn alle Coroutinen erschöpft sind (abgeschlossen / beendet, alle Werte zurückgegeben).
loop = get_event_loop() #
loop.run_until_complete(wait(coros))
Die run_until_complete
Methode würde sicherstellen, dass die Schleife aktiv ist, bis die Ausführung abgeschlossen ist. Bitte beachten Sie, dass Sie in diesem Fall nicht die Ergebnisse der asynchronen Ausführung erhalten.
Im zweiten Beispiel verwenden Sie die ensure_future
Funktion, um eine Coroutine zu verpacken und ein Task
Objekt zurückzugeben, das eine Art ist Future
. Die Coroutine soll beim Aufruf in der Hauptereignisschleife ausgeführt werden ensure_future
. Das zurückgegebene Future / Task-Objekt hat noch keinen Wert, aber im Laufe der Zeit, wenn die Netzwerkoperationen beendet sind, enthält das Future-Objekt das Ergebnis der Operation.
from asyncio import ensure_future
futures = []
for i in range(5):
futures.append(ensure_future(foo(i)))
loop = get_event_loop()
loop.run_until_complete(wait(futures))
In diesem Beispiel machen wir dasselbe, außer dass wir Futures verwenden, anstatt nur Coroutinen zu verwenden.
Schauen wir uns ein Beispiel für die Verwendung von Asyncio / Coroutines / Futures an:
import asyncio
async def slow_operation():
await asyncio.sleep(1)
return 'Future is done!'
def got_result(future):
print(future.result())
# We have result, so let's stop
loop.stop()
loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)
# We run forever
loop.run_forever()
Hier haben wir die create_task
Methode für das loop
Objekt verwendet. ensure_future
würde die Aufgabe in der Hauptereignisschleife planen. Diese Methode ermöglicht es uns, eine Coroutine in einer von uns ausgewählten Schleife zu planen.
Wir sehen auch das Konzept des Hinzufügens eines Rückrufs mithilfe der add_done_callback
Methode für das Task-Objekt.
A Task
ist, done
wenn die Coroutine einen Wert zurückgibt, eine Ausnahme auslöst oder abgebrochen wird. Es gibt Methoden, um diese Vorfälle zu überprüfen.
Ich habe einige Blog-Beiträge zu diesen Themen geschrieben, die helfen könnten:
Weitere Details finden Sie natürlich im offiziellen Handbuch: https://docs.python.org/3/library/asyncio.html
ensure_future()
? Und wenn ich das Ergebnis brauche, kann ich es nicht einfach verwendenrun_until_complete(gather(coros))
?ensure_future
plant die Ausführung der Coroutine in der Ereignisschleife. Also würde ich ja sagen, es ist erforderlich. Natürlich können Sie die Coroutinen auch mit anderen Funktionen / Methoden planen. Ja, Sie können verwendengather()
- aber sammeln wird warten, bis alle Antworten gesammelt sind.gather
undwait
verpacken die angegebenen Coroutinen tatsächlich als Aufgaben mitensure_future
(siehe die Quellen hier und hier ). Es macht also keinen Sinn,ensure_future
vorher zu verwenden, und es hat nichts damit zu tun, die Ergebnisse zu erhalten oder nicht.ensure_future
eine hatloop
Argument, so gibt es keinen Grund zu verwenden ,loop.create_task
überensure_future
. Undrun_in_executor
funktioniert nicht mit Coroutinen, stattdessen sollte ein Semaphor verwendet werden.create_task
über zu verwendenensure_future
, siehe Dokumente . Zitatcreate_task() (added in Python 3.7) is the preferable way for spawning new tasks.
Einfache Antwort
async def
) aufrufen, wird sie NICHT ausgeführt. Es gibt Coroutine-Objekte zurück, so wie die Generatorfunktion Generatorobjekte zurückgibt.await
Ruft Werte von Coroutinen ab, dh "ruft" die Coroutine aufeusure_future/create_task
Planen Sie die Coroutine so, dass sie bei der nächsten Iteration in der Ereignisschleife ausgeführt wird (obwohl Sie nicht darauf warten, dass sie beendet wird, wie bei einem Daemon-Thread).Einige Codebeispiele
Lassen Sie uns zunächst einige Begriffe klären:
async def
;Fall 1
await
auf einer CoroutineWir erstellen zwei Coroutinen,
await
eine, und verwenden siecreate_task
, um die andere auszuführen.Sie erhalten Ergebnis:
Erklären:
Task1 wurde direkt ausgeführt, und Task2 wurde in der folgenden Iteration ausgeführt.
Fall 2, der die Kontrolle über die Ereignisschleife ergibt
Wenn wir die Hauptfunktion ersetzen, sehen wir ein anderes Ergebnis:
Sie erhalten Ergebnis:
Erklären:
Beim Aufruf
asyncio.sleep(1)
wurde das Steuerelement an die Ereignisschleife zurückgegeben, und die Schleife prüft, ob Aufgaben ausgeführt werden sollen, und führt dann die von erstellte Aufgabe auscreate_task
.Beachten Sie, dass wir zuerst die Coroutine-Funktion aufrufen, aber nicht
await
, also haben wir nur eine einzelne Coroutine erstellt und sie nicht zum Laufen gebracht. Dann rufen wir die Coroutine-Funktion erneut auf und schließen sie in einencreate_task
Aufruf ein. Creat_task plant tatsächlich, dass die Coroutine bei der nächsten Iteration ausgeführt wird. Also, im Ergebniscreate task
wird vorher ausgeführtawait
.Eigentlich geht es hier darum, der Schleife die Kontrolle zurückzugeben, mit der Sie
asyncio.sleep(0)
das gleiche Ergebnis sehen können.Unter der Haube
loop.create_task
ruft tatsächlich anasyncio.tasks.Task()
, was anrufen wirdloop.call_soon
. Undloop.call_soon
wird die Aufgabe in setzenloop._ready
. Während jeder Iteration der Schleife wird nach allen Rückrufen in loop._ready gesucht und ausgeführt.asyncio.wait
,asyncio.ensure_future
Undasyncio.gather
rufen tatsächlichloop.create_task
direkt oder indirekt.Beachten Sie auch in den Dokumenten :
quelle
await task2
Anrufs könnte geklärt werden. In beiden Beispielen ist der Aufruf von loop.create_task () das, was task2 in der Ereignisschleife plant. Also kannst du in beiden Exs die löschenawait task2
und trotzdem wird task2 irgendwann ausgeführt. In Beispiel 2 ist das Verhalten identisch, daawait task2
ich glaube, dass nur die bereits abgeschlossene Aufgabe geplant wird (die nicht ein zweites Mal ausgeführt wird), während in Beispiel 1 das Verhalten geringfügig anders sein wird, da Aufgabe 2 erst ausgeführt wird, wenn main abgeschlossen ist. Um den Unterschied zu sehen, fügen Sieprint("end of main")
am Ende von ex1's main hinzuEin Kommentar von Vincent, der mit https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346 verlinkt ist und zeigt, dass
wait()
die Coroutinenensure_future()
für Sie eingepackt sind !Mit anderen Worten, wir brauchen eine Zukunft, und Coroutinen werden stillschweigend in sie umgewandelt.
Ich werde diese Antwort aktualisieren, wenn ich eine endgültige Erklärung zum Batching von Coroutinen / Futures finde.
quelle
c
,await c
entsprichtawait create_task(c)
?Aus der BDFL [2013]
Aufgaben
In diesem Sinne
ensure_future
ist es sinnvoll, einen Namen für die Erstellung einer Aufgabe zu erstellen, da das Ergebnis der Zukunft berechnet wird, unabhängig davon, ob Sie darauf warten oder nicht (solange Sie auf etwas warten). Auf diese Weise kann die Ereignisschleife Ihre Aufgabe abschließen, während Sie auf andere Dinge warten. Beachten Sie, dass in Python 3.7create_task
der bevorzugte Weg ist, um eine Zukunft zu sichern .Hinweis: Ich habe "Ertrag von" in Guidos Folien geändert, um hier auf die Moderne zu "warten".
quelle