2016-04-28 5 views
6

Supponiamo di trovarti nella sfortunata posizione di avere una dipendenza da una libreria con cattive abitudini. Il tuo codice deve chiamare FlakyClient.call(), ma a volte tale funzione finisce per impiccarsi per una quantità di tempo inaccettabile.Come si può salvaguardare da una chiamata di libreria traballante che potrebbe bloccarsi indefinitamente?

Come mostrato di seguito, un modo per aggirare questo è quello di avvolgere la chiamata nel proprio processo , e utilizzare il parametro timeout nel unirsi metodo per definire un tempo massimo che siete disposti ad aspettare il FlakyClient. Ciò fornisce una buona protezione, ma impedisce anche che il corpo principale del codice reagisca al risultato della chiamata a FlakyClient.call(). L'unico modo che conosco per affrontare questo altro problema (ottenere il risultato nel corpo principale del codice) è usare una tecnica ingombrante di IPC.

Qual è un modo pulito e pitonico per gestire questi due problemi? Voglio proteggermi se la chiamata alla libreria si blocca, ed essere in grado di usare il risultato se la chiamata termina.

Grazie!

from multiprocessing import Process 
from flaky.library import FlakyClient 


TIMEOUT_IN_SECS = 10 

def make_flaky_call(): 
    result = FlakyClient.call() 

proc = Process(target=make_flaky_call) 
proc.start() 
proc.join(TIMEOUT_IN_SECS) 
if proc.is_alive(): 
    proc.terminate() 
    raise Exception("Timeout during call to FlakyClient.call().") 
+0

È possibile utilizzare thread o costrutti leggeri come greenlet. –

+0

Invece di aspettare, potresti chiamarlo un po 'prima, quindi ne avrai bisogno e vedrai se ha terminato l'elaborazione prima che sia necessario? –

+0

Mi piace pensare che questa domanda possa essere meglio risolta usando i cicli degli eventi di 'asyncio' ed eseguendo le funzioni in modo asincrono (si veda [esempio] (http://stackabuse.com/python-async-await-tutorial/)), ma 'asyncio' non è disponibile prima di Python 3.4. –

risposta

2

Se si utilizza Processo, suggerirei di utilizzare un Queue per gestire il trasferimento dei risultati e indirettamente anche gestire il timeout della funzione.

from multiprocessing import Process, Queue 
from flaky.library import FlakyClient 
import time 

TIMEOUT_IN_SECS = 10 

def make_flaky_call(queue): 
    result = FlakyClient.call() 
    queue.put(result) 
    queue.put('END') 

q = Queue() 
proc = Process(target=make_flaky_call, args=(q,)) 
proc.start() 
content = 0 
result = None 
while content != 'END': 
    try: 
     content = q.get(timeout=TIMEOUT_IN_SECS) 
     if content != 'END': 
      result = content 
    except Empty: 
     proc.terminate() 
     raise Exception("Timeout during call to FlakyClient.call().") 
4

Non posso parlare a Python 2.7, ma in Python 3, il corretta modo per gestire questa situazione è di fare uso di asyncio e il concetto di futures.

import concurrent 

def make_flaky_call(): 
    return FlakyClient.call() 

timeout = 10 

with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: 
    future = executor.submit(make_flaky_call) # get a future object 
    try: 
     result = await future.result(timeout = timeout) 
    except concurrent.futures.TimeOutError: 
     # if a timeout occurs on the call, do something 
     result = None # default value 

Questo è piuttosto Pythonic. È possibile integrare questo con il corpo principale del codice. Utilizza correttamente try-except per la gestione degli errori. Viene fornito con un timeout integrato. Funziona solo in Python 3.5 (grazie a await - ma il passaggio a yield from lo rende compatibile con Python 3.4).

Per Python 2.7, sfortunatamente, il modo corretto per gestirlo è quello di fare ciò che si sta facendo attualmente.

+0

Grazie per la spiegazione di Python 3. Questo approccio è ciò che sono abituato a vedere in Java-land. Sfortunatamente sto ancora lavorando al codice Python 2.7. –

Problemi correlati