2011-12-23 10 views
6

ho questo decoratore presa direttamente da un esempio che ho trovato in rete:Time out decoratore su una funzione multprocessing

class TimedOutExc(Exception): 
    pass 


def timeout(timeout): 
    def decorate(f): 
     def handler(signum, frame): 
      raise TimedOutExc() 

     def new_f(*args, **kwargs): 

      old = signal.signal(signal.SIGALRM, handler) 
      signal.alarm(timeout) 

      try: 
       result = f(*args, **kwargs) 
      except TimedOutExc: 
       return None 
      finally: 
       signal.signal(signal.SIGALRM, old) 
      signal.alarm(0) 
      return result 

     new_f.func_name = f.func_name 
     return new_f 

    return decorate 

Si genera un'eccezione se i tempi di funzione f fuori.

Bene, funziona, ma quando uso questo decoratore su una funzione di multiprocessing e si interrompe a causa di un timeout, non termina i processi coinvolti nel calcolo. Come lo posso fare?

Non voglio avviare un'eccezione e interrompere il programma. Fondamentalmente quello che voglio è quando f scade, farlo restituire None e quindi terminare i processi coinvolti.

risposta

9

Mentre sono d'accordo con il punto principale della risposta di Aaron, vorrei approfondire un po '.

I processi lanciati da multiprocessing deve essere fermato nella funzione da decorare; Non penso che ciò possa essere fatto in generale e semplicemente dal decoratore stesso (la funzione decorata è l'unica entità che sa quali calcoli ha lanciato).

Invece di fare in modo che la funzione decorata catturi SIGALARM, è anche possibile rilevare l'eccezione personalizzata TimedOutExc, che potrebbe essere più flessibile. Il tuo esempio diventerebbe quindi:

class TimedOutExc(Exception): 
    """ 
    Raised when a timeout happens 
    """ 

def timeout(timeout): 
    """ 
    Return a decorator that raises a TimedOutExc exception 
    after timeout seconds, if the decorated function did not return. 
    """ 

    def decorate(f): 

     def handler(signum, frame): 
      raise TimedOutExc() 

     def new_f(*args, **kwargs): 

      old_handler = signal.signal(signal.SIGALRM, handler) 
      signal.alarm(timeout) 

      result = f(*args, **kwargs) # f() always returns, in this scheme 

      signal.signal(signal.SIGALRM, old_handler) # Old signal handler is restored 
      signal.alarm(0) # Alarm removed 

      return result 

     new_f.func_name = f.func_name 
     return new_f 

    return decorate 

@timeout(10) 
def function_that_takes_a_long_time(): 
    try: 
     # ... long, parallel calculation ... 
    except TimedOutExc: 
     # ... Code that shuts down the processes ... 
     # ... 
     return None # Or exception raised, which means that the calculation is not complete 
+1

Sembra che funzioni. Conservo il mio decoratore perché il tuo non restituisce Nessuno in una funzione non parallela a meno che non gestisca sempre l'eccezione nella funzione. Quello che ho fatto è gestire l'eccezione come hai fatto solo nella parte del codice di calcolo parallelo della funzione e il gioco è fatto. Grazie mille. Molto chiaro. – blueSurfer

+0

Grazie per aver condiviso. Mantenere l'arredatore originale ha davvero senso. – EOL

2

Dubito che si possa fare con un decoratore: un decoratore è un involucro per una funzione; la funzione è una scatola nera. Non c'è comunicazione tra il decoratore e la funzione che avvolge.

Quello che dovete fare è riscrivere il codice della vostra funzione per utilizzare il gestore SIGALRM di interrompere qualsiasi processo è iniziato.

+2

Questo non è del tutto preciso. Puoi passare valori ai generatori usando il metodo 'send'. Se una funzione si ripete ripetutamente, puoi scrivere un decoratore che lo avvolge in un ciclo while e indicare alla funzione di terminare dopo ogni resa. In realtà non dovresti inviare un valore, potresti semplicemente interrompere il ciclo. Non raccomando questa soluzione nella maggior parte dei casi, non sarebbe molto diverso dal fatto che una funzione tiene traccia del proprio tempo di esecuzione. Non sono sicuro di come si applicherebbe al multiprocessing, il wrapper dovrebbe essere eseguito nello stesso processo. Ma è possibile. – mikerobi

Problemi correlati