2015-10-26 20 views
7

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 '?

+4

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'. –

risposta

17

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 
+0

Ci scusiamo per la risposta in ritardo conferma. Grazie per la spiegazione che mi ha aiutato molto! – dalanmiller

-1

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 con async 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 per n secondi). La funzione usuale get_x non è in grado di comunicare con il loop eventi.
Problemi correlati