Sto ottenendo il flusso usando asyncio
in Python 3.5 ma non ho visto una descrizione di cosa dovrei essere await
e cose che non dovrei essere o dove sarebbe trascurabile. Devo solo usare il mio miglior giudizio in termini di "questa è un'operazione IO e quindi dovrebbe essere await
ed"?Quando utilizzare e quando non utilizzare Python 3.5 `attendere '?
risposta
Per impostazione predefinita tutto il codice è sincrono. È possibile renderle funzioni di definizione asincrona con async def
e chiamando questa funzione con await
. La domanda più corretta è "Quando dovrei scrivere codice asincrono invece di sincrono?". La risposta è "Quando puoi beneficiarne". Nella maggior parte dei casi come avrete notato otterrete vantaggio, quando si lavora con operazioni di I/O:
# 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)
Naturalmente, se avete creato la funzione che utilizza il codice asincrono, questa funzione dovrebbe essere troppo asincrono (dovrebbe essere definito come async def
). Ma qualsiasi funzione asincrona può liberamente utilizzare il codice sincrono. Non ha senso per lanciare codice sincrono asincrono senza qualche ragione:
# 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
Una cosa molto importante è che qualsiasi operazione lunga sincrona (> 50 ms, per esempio, è difficile dire esattamente) si blocca tutto il vostro asincrona operazioni per quel tempo:
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)
si può evitare di chiamare a lungo in esecuzione funzioni sincrone nel processo separato (e in attesa per il risultato):
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)
Un altro esempio: quando è necessario utilizzare requests
in asyncio. requests.get
è solo una funzione di lunga durata sincrona, che non si dovrebbe chiamare all'interno del codice asincrono (di nuovo, per evitare il congelamento). Ma sta funzionando a lungo a causa di I/O, non a causa di lunghi calcoli. In tal caso, è possibile utilizzare ThreadPoolExecutor
invece di ProcessPoolExecutor
per evitare un sovraccarico multiprocessing:
executor = ThreadPoolExecutor(2)
async def download(url):
response = await loop.run_in_executor(executor, requests.get, url)
return response.text
Ci scusiamo per la risposta in ritardo conferma. Grazie per la spiegazione che mi ha aiutato molto! – dalanmiller
Non avete molta libertà. Se hai bisogno di chiamare una funzione, devi scoprire se questa è una funzione normale o una coroutine. È necessario utilizzare la parola chiave await
se e solo se la funzione che si sta chiamando è una coroutine.
Se sono coinvolte le funzioni async
, dovrebbe esserci un "ciclo di eventi" che orchestra queste funzioni async
. A rigor di termini non è necessario, è possibile eseguire "manualmente" il metodo async
inviando valori, ma probabilmente non si vuole farlo. Il ciclo degli eventi tiene traccia delle coroutine non ancora ultimate e sceglie il prossimo per continuare a correre. Il modulo asyncio
fornisce un'implementazione del ciclo degli eventi, ma questa non è l'unica implementazione possibile.
Considerare questi due righe di codice:
x = get_x()
do_something_else()
e
x = await aget_x()
do_something_else()
semantico è assolutamente lo stesso: chiamare un metodo che produce un certo valore, quando il valore è pronto assegnare alla variabile x
e fare qualcos'altro In entrambi i casi, la funzione do_something_else
verrà chiamata solo al termine della riga di codice precedente.Non significa nemmeno che prima o dopo o durante l'esecuzione del metodo asincrono aget_x
il controllo verrà restituito al ciclo di eventi.
Ancora ci sono alcune differenze:
- secondo frammento può apparire solo all'interno di un'altra funzione
async
aget_x
funzione non è usuale, ma coroutine (cioè sia dichiarata conasync
parola o decorato come coroutine)aget_x
è in grado di "comunicare" con il ciclo degli eventi: questo significa cedere alcuni oggetti ad esso. Il ciclo degli eventi dovrebbe essere in grado di interpretare questi oggetti come richieste per eseguire alcune operazioni (ad es. Per inviare una richiesta di rete e attendere la risposta, o semplicemente sospendere questa coroutine pern
secondi). La funzione usualeget_x
non è in grado di comunicare con il loop eventi.
Leggi [PEP 492] (https://www.python.org/dev/peps/pep-0492/#id50) per i dettagli, ma in generale dovresti "attendere" su tutti i Futures, '@coroutine 'funzioni decorate e funzioni' async def'. –