2011-11-23 23 views
7

Voglio timeout di un particolare pezzo di codice Python dopo in esecuzione per 0,5 secondi. Quindi intendo sollevare un'eccezione/segnale dopo 0,5 secondi e gestirlo con garbo e continuare con il resto del codice.Come segnalare un allarme in Python 2.4 dopo 0,5 secondi

In python so che signal.alarm() può impostare l'allarme per i secondi interi. C'è qualche alternativa in cui possiamo generare un allarme dopo 0,5 secondi. signal.setitimer() come suggerito in altri post non è disponibile in python2.4 e ho bisogno di usare python2.4 per questo scopo?

+0

scrivere un'estensione C è un'opzione ... – pajton

+1

Per chi ha votato per chiudere questo problema sostenendo l'idea è un esatto duplicato di [questo uno ] (http://stackoverflow.com/q/3770711/146792): hai effettivamente letto la domanda OP fino alla fine? La domanda è completamente valida. – mac

+0

@mac: Penso che la persona che ha votato per chiudere abbia già capito il suo errore - almeno ha cancellato il commento generato automaticamente. :) –

risposta

2

si hanno due opzioni:

  1. Polling time.time() o simili, mentre il codice in questione viene eseguito. Questo è ovviamente fattibile solo se quel codice è sotto il tuo controllo.

  2. Come menzionato da pajton, è possibile scrivere un'estensione C per chiamare la chiamata di sistema setitimer(). Questo non è troppo difficile perché potresti semplicemente copiare il codice di signal.getitimer() e signal.setitimer() dall'origine delle versioni successive di Python. Sono solo involucri sottili attorno alle chiamate di sistema con lo stesso nome.

    Questa opzione è valida solo se si utilizza CPython e ci si trova in un ambiente che consente di utilizzare estensioni C personalizzate.

    Edit: Ecco il codice copiato da signalmodule.c in Python 2.7 (licenza di Python si applica):

    #include "Python.h" 
    #include <sys/time.h> 
    
    static PyObject *ItimerError; 
    
    /* auxiliary functions for setitimer/getitimer */ 
    static void 
    timeval_from_double(double d, struct timeval *tv) 
    { 
        tv->tv_sec = floor(d); 
        tv->tv_usec = fmod(d, 1.0) * 1000000.0; 
    } 
    
    Py_LOCAL_INLINE(double) 
    double_from_timeval(struct timeval *tv) 
    { 
        return tv->tv_sec + (double)(tv->tv_usec/1000000.0); 
    } 
    
    static PyObject * 
    itimer_retval(struct itimerval *iv) 
    { 
        PyObject *r, *v; 
    
        r = PyTuple_New(2); 
        if (r == NULL) 
        return NULL; 
    
        if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) { 
        Py_DECREF(r); 
        return NULL; 
        } 
    
        PyTuple_SET_ITEM(r, 0, v); 
    
        if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) { 
        Py_DECREF(r); 
        return NULL; 
        } 
    
        PyTuple_SET_ITEM(r, 1, v); 
    
        return r; 
    } 
    
    static PyObject * 
    itimer_setitimer(PyObject *self, PyObject *args) 
    { 
        double first; 
        double interval = 0; 
        int which; 
        struct itimerval new, old; 
    
        if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval)) 
        return NULL; 
    
        timeval_from_double(first, &new.it_value); 
        timeval_from_double(interval, &new.it_interval); 
        /* Let OS check "which" value */ 
        if (setitimer(which, &new, &old) != 0) { 
        PyErr_SetFromErrno(ItimerError); 
        return NULL; 
        } 
    
        return itimer_retval(&old); 
    } 
    
    PyDoc_STRVAR(setitimer_doc, 
    "setitimer(which, seconds[, interval])\n\ 
    \n\ 
    Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\ 
    or ITIMER_PROF) to fire after value seconds and after\n\ 
    that every interval seconds.\n\ 
    The itimer can be cleared by setting seconds to zero.\n\ 
    \n\ 
    Returns old values as a tuple: (delay, interval)."); 
    
    static PyObject * 
    itimer_getitimer(PyObject *self, PyObject *args) 
    { 
        int which; 
        struct itimerval old; 
    
        if (!PyArg_ParseTuple(args, "i:getitimer", &which)) 
        return NULL; 
    
        if (getitimer(which, &old) != 0) { 
        PyErr_SetFromErrno(ItimerError); 
        return NULL; 
        } 
    
        return itimer_retval(&old); 
    } 
    
    PyDoc_STRVAR(getitimer_doc, 
    "getitimer(which)\n\ 
    \n\ 
    Returns current value of given itimer."); 
    
    static PyMethodDef itimer_methods[] = { 
        {"setitimer",  itimer_setitimer, METH_VARARGS, setitimer_doc}, 
        {"getitimer",  itimer_getitimer, METH_VARARGS, getitimer_doc}, 
        {NULL,      NULL}   /* sentinel */ 
    }; 
    
    PyMODINIT_FUNC 
    inititimer(void) 
    { 
        PyObject *m, *d, *x; 
        int i; 
        m = Py_InitModule3("itimer", itimer_methods, 0); 
        if (m == NULL) 
         return; 
    
        d = PyModule_GetDict(m); 
    
    #ifdef ITIMER_REAL 
        x = PyLong_FromLong(ITIMER_REAL); 
        PyDict_SetItemString(d, "ITIMER_REAL", x); 
        Py_DECREF(x); 
    #endif 
    #ifdef ITIMER_VIRTUAL 
        x = PyLong_FromLong(ITIMER_VIRTUAL); 
        PyDict_SetItemString(d, "ITIMER_VIRTUAL", x); 
        Py_DECREF(x); 
    #endif 
    #ifdef ITIMER_PROF 
        x = PyLong_FromLong(ITIMER_PROF); 
        PyDict_SetItemString(d, "ITIMER_PROF", x); 
        Py_DECREF(x); 
    #endif 
    
        ItimerError = PyErr_NewException("itimer.ItimerError", 
                PyExc_IOError, NULL); 
        if (ItimerError != NULL) 
         PyDict_SetItemString(d, "ItimerError", ItimerError); 
    } 
    

    Salva questo codice come itimermodule.c, compilarlo a un'estensione C usando qualcosa come

    gcc -I /usr/include/python2.4 -fPIC -o itimermodule.o -c itimermodule.c 
    gcc -shared -o itimer.so itimermodule.o -lpython2.4 
    

    Ora, se sei fortunato, dovresti riuscire a importarlo da Python usando

    import itimer 
    

    e chiamare itimer.setitimer().

+0

Assolutamente non testato: ma per quanto riguarda l'esecuzione del codice da eliminare se il segnale si verifica sotto un thread e l'esecuzione di un timer (polling 'time.time()') nel codice principale? Il timer potrebbe uccidere il thread una volta raggiunto il limite ... [brutto ma ... non potrebbe funzionare?] – mac

+0

@mac: No, questo non funziona. Non puoi uccidere discussioni in Python. –

+0

@sven: il codice non sarà sotto il mio controllo. Ci sono molti calcoli e chiamate di funzione coinvolte, ed è difficile fare sondaggi in un singolo posto. –

4

Sollevare l'allarme da un thread "daemon" pazientemente in attesa. Nel codice qui sotto, snoozealarm fa ciò che si vuole attraverso un filo SnoozeAlarm:

#! /usr/bin/env python 

import os 
import signal 
import threading 
import time 

class SnoozeAlarm(threading.Thread): 
    def __init__(self, zzz): 
    threading.Thread.__init__(self) 
    self.setDaemon(True) 
    self.zzz = zzz 

    def run(self): 
    time.sleep(self.zzz) 
    os.kill(os.getpid(), signal.SIGALRM) 

def snoozealarm(i): 
    SnoozeAlarm(i).start() 

def main(): 
    snoozealarm(0.5) 
    while True: 
    time.sleep(0.05) 
    print time.time() 


if __name__ == '__main__': 
    main() 
+0

Buona idea, +1 .. –

+0

Questo è ciò che chiamo ingegneria "inversa". +1;) – mac

Problemi correlati