2015-09-16 10 views
11

Sto provando a fare un sacco di richieste (~ 1000) usando Asyncio e la libreria aiohttp, ma sto incontrando un problema su cui non riesco a trovare molte informazioni.Asyncio RuntimeError: Event Loop è chiuso

Quando eseguo questo codice con 10 URL, funziona perfettamente. Quando lo eseguo con 100+ urls, si rompe e mi dà l'errore RuntimeError: Event loop is closed.

import asyncio 
import aiohttp 


@asyncio.coroutine 
def get_status(url): 
    code = '000' 
    try: 
     res = yield from asyncio.wait_for(aiohttp.request('GET', url), 4) 
     code = res.status 
     res.close() 
    except Exception as e: 
     print(e) 
    print(code) 


if __name__ == "__main__": 
    urls = ['https://google.com/'] * 100 
    coros = [asyncio.Task(get_status(url)) for url in urls] 
    loop = asyncio.get_event_loop() 
    loop.run_until_complete(asyncio.wait(coros)) 
    loop.close() 

L'analisi dello stack può essere trovato here.

Qualsiasi aiuto o intuizione sarebbe molto apprezzato visto che ho battuto la testa per ore. Ovviamente questo suggerirebbe che un ciclo di eventi è stato chiuso che dovrebbe essere ancora aperto, ma non vedo come sia possibile.

+0

non è 'errore Asyncio'. Errore ricorsivo Python, limite raggiunto. necessario thread per tutte le funzioni non class ... – dsgdfg

+0

Prima di tutto, assicurati di utilizzare l'ultima versione di aiohttp. Suppongo che tu lo faccia. Tecnicamente aiohttp ha bisogno di un ciclo iterativo dopo aver terminato la richiesta di chiusura dei socket sottostanti. Quindi inserire 'loop.run_until_complete (asyncio.sleep (0))' prima della chiamata 'loop.close()'. –

+0

Il traceback suggerisce che un lavoro inviato a un [Executor] (https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor) tramite [run_in_executor] (https: // docs .python.org/3/library/asyncio-eventloop.html # asyncio.BaseEventLoop.run_in_executor) restituiti dopo che il ciclo è stato chiuso. Stranamente, [aiohttp] (https://github.com/KeepSafe/aiohttp/search?utf8=%E2%9C%93&q=run_in_executor&type=Code) e [asyncio] (https://github.com/python/asyncio/search? utf8 =% E2% 9C% 93 & q = run_in_executor) non usare 'run_in_executor' ... – Vincent

risposta

3

Hai ragione, loop.getaddrinfo utilizza un ThreadPoolExecutor per eseguire socket.getaddrinfo in una discussione.

Si sta usando asyncio.wait_for con un timeout, il che significa che res = yield from asyncio.wait_for... aumenterà un asyncio.TimeoutError dopo 4 secondi. Quindi le corineutine get_status restituiscono None e gli arresti del ciclo. Se un lavoro termina dopo, tenterà di pianificare una richiamata nel ciclo degli eventi e solleva un'eccezione poiché è già chiusa.

+0

Ah, questo ha senso, ma questo è l'unico modo che ho trovato per implementare i timeout delle richieste. Sai di un modo in cui potrei timeout senza chiudere il ciclo? –

+0

@PatrickAllen Potresti voler aumentare il [numero di lavoratori] (https://github.com/python/asyncio/blob/27f3499f968e8734fef91677eb339b5d32a6f675/asyncio/base_events.py#L44) che è 5 per impostazione predefinita. – Vincent

+2

@PatrickAllen Oppure utilizzare 'loop._default_executor.shutdown (wait = True)' prima di chiudere il ciclo. – Vincent

16

Il bug è archiviato come https://github.com/python/asyncio/issues/258 Restate sintonizzati.

Come soluzione rapida suggerisco di utilizzare l'esecutore personalizzato, ad es.

loop = asyncio.get_event_loop() 
executor = concurrent.futures.ThreadPoolExecutor(5) 
loop.set_default_executor(executor) 

Prima di terminare il programma si prega di fare

executor.shutdown(wait=True) 
loop.close() 
+0

Awesome Andrew, grazie per il vostro aiuto. Non avevo capito che stavo parlando con una parte della squadra :). Seguendo questo su GH –

+0

'Modificato nella versione 3.5.3: BaseEventLoop.run_in_executor() non configura più i max_workers dell'esecutore del pool di thread che crea' – RomanPerekhrest

+0

Andrew, puoi suggerire di non" soluzione rapida "ma qualche soluzione efficace per Python 3.5? – RomanPerekhrest

Problemi correlati