2015-10-26 32 views
7

Ich bekomme den Fluss von asyncio in Python 3.5, aber ich habe keine Beschreibung der Dinge, die ich sein sollte und Dinge, die ich nicht sein sollte oder wo es vernachlässigbar wäre. Muss ich nur mein bestes Urteil in Bezug auf "Dies ist eine IO-Operation und sollte daher await ED" sein?Wann zu verwenden und wann nicht Python 3.5 `erwarten 'verwenden?

+4

Lesen Sie [PEP 492] (https://www.python.org/dev/peps/pep-0492/#id50) für Details, aber im Allgemeinen sollten Sie 'erwarten' auf allen Futures, '@coroutine 'dekorierte Funktionen &' async def' Funktionen. –

Antwort

17

Standardmäßig ist Ihr gesamter Code synchron. Sie können asynchrone definierende Funktionen mit async def machen und diese Funktionen mit await aufrufen. Korrektere Frage ist "Wann sollte ich asynchronen Code anstelle von synchron schreiben?". Die Antwort lautet: "Wenn Sie davon profitieren können". In den meisten Fällen, wie Sie bemerkt, Sie Vorteil bekommen, wenn Sie mit I/O-Operationen arbeiten:

# Synchronous way: 
download(url1) # takes 5 sec. 
download(url2) # takes 5 sec. 
# Total time: 10 sec. 

# Asynchronous way: 
await asyncio.gather(
    download(url1), # takes 5 sec. 
    download(url2) # takes 5 sec. 
) 
# Total time: only 5 sec. (+ little overhead for using asyncio) 

Natürlich, wenn Sie Funktion erstellt, den asynchronen Code verwendet, sollte diese Funktion auch asynchron sein (sollte so definiert werden, async def). Aber jede asynchrone Funktion kann frei synchronen Code verwenden. Es macht keinen Sinn, ohne irgendeinen Grund asynchron synchron Code zu werfen:

# extract_links(url) should be async because it uses async func download() inside 
async def extract_links(url): 
    # download() was created async to get benefit of I/O 
    data = await download(url) 
    # parse() doesn't work with I/O, no sense to make it async 
    links = parse(data) 
    return links 

Eine sehr wichtige Sache ist, dass jeder langSynchronLauf (> 50 ms, zum Beispiel, ist es schwer, genau zu sagen) wird all Ihre asynchronen einfrieren Operationen für diese Zeit:

async def extract_links(url): 
    data = await download(url) 
    links = parse(data) 
    # if search_in_very_big_file() takes much time to process, 
    # all your running async funcs (somewhere else in code) will be friezed 
    # you need to avoid this situation 
    links_found = search_in_very_big_file(links) 

Sie können es vermeiden Aufruf lange synchrone Funktionen in separaten Prozess ausgeführt wird (und warten auf Ergebnis):

executor = ProcessPoolExecutor(2) 

async def extract_links(url): 
    data = await download(url) 
    links = parse(data) 
    # Now your main process can handle another async functions while separate process running  
    links_found = await loop.run_in_executor(executor, search_in_very_big_file, links) 

Ein weiteres Beispiel: Wenn Sie requests in asyncio verwenden müssen. requests.get ist nur synchrone Long-Running-Funktion, die Sie nicht innerhalb Async-Code aufrufen sollten (wieder, um Einfrieren zu vermeiden). Aber es läuft wegen I/O lange, nicht wegen langer Berechnungen. In diesem Fall können Sie ThreadPoolExecutor statt ProcessPoolExecutor verwenden, um einige Multiprozessing Overhead zu vermeiden:

executor = ThreadPoolExecutor(2) 

async def download(url): 
    response = await loop.run_in_executor(executor, requests.get, url) 
    return response.text 
+0

Entschuldigung für die verspätete Antwort bestätigen. Danke für die Erklärung, die mir sehr geholfen hat! – dalanmiller

-1

Sie haben noch viel Freiheit. Wenn Sie eine Funktion aufrufen müssen, müssen Sie herausfinden, ob dies eine normale Funktion oder eine Routine ist. Sie müssen das Schlüsselwort await genau dann verwenden, wenn die von Ihnen aufgerufene Funktion eine Coroutine ist.

Wenn async Funktionen beteiligt sind, sollte es eine "Ereignisschleife" geben, die diese async Funktionen orchestriert. Streng genommen ist es nicht notwendig, Sie können "manuell" die async Methode ausführen, die Werte an sie sendet, aber wahrscheinlich möchten Sie es nicht tun. Die Ereignisschleife verfolgt die noch nicht abgeschlossenen Koroutinen und wählt die nächste aus, um weiter zu laufen. asyncio Modul bietet eine Implementierung von Ereignisschleife, aber dies ist nicht die einzige mögliche Implementierung.

Betrachten Sie diese zwei Zeilen Code:

x = get_x() 
do_something_else() 

und

x = await aget_x() 
do_something_else() 

Semantic ist absolut das gleiche: eine Methode aufrufen, die einen Wert erzeugt, wenn der Wert, den es Variable bereit zuweisen ist x und etwas anderes machen. In beiden Fällen wird die do_something_else-Funktion erst aufgerufen, nachdem die vorherige Codezeile beendet wurde.Es bedeutet nicht einmal, dass vor oder nach oder während der Ausführung der asynchronen aget_x Methode die Kontrolle der Ereignisschleife übergeben wird.

Dennoch gibt es einige Unterschiede:

  • den zweiten Schnipsel nur in einer anderen async Funktion
  • aget_x Funktion nicht gewohnt angezeigt werden kann, aber Koroutine (das entweder mit async Schlüsselwort oder dekoriert als Koroutine deklariert)
  • aget_x ist in der Lage zu "kommunizieren" mit der Ereignisschleife: das sind einige Objekte dazu. Die Ereignisschleife sollte in der Lage sein, diese Objekte als Anforderungen zu interpretieren, um einige Operationen auszuführen (z. B. um eine Netzwerkanforderung zu senden und auf eine Antwort zu warten, oder einfach diese Coroutine für n Sekunden anzuhalten). Übliche get_x Funktion ist nicht in der Lage, mit Ereignisschleife zu kommunizieren.
Verwandte Themen