2011-11-23 12 views
7

Chcę przeterminować określony fragment kodu Pythona po uruchomieniu na 0,5 sekundy. Więc zamierzam podnieść wyjątek/sygnał po 0,5 sekundy, i poradzić sobie z wdziękiem i kontynuować z resztą kodu.Jak zasygnalizować alarm w pythonie 2.4 po 0,5 sekundy

W python wiem, że signal.alarm() może ustawić alarm na sekundy całkowite. Czy istnieje alternatywa, w której możemy wygenerować alarm po 0,5 sekundy. signal.setitimer() jak zasugerowano w innych postach nie jest dostępny w python2.4 i muszę użyć python2.4 w tym celu?

+0

pisanie rozszerzenia C jest jedna opcja ... – pajton

+1

każdemu, kto głosował, aby zamknąć to pytanie wspieranie idei jest dokładną kopią [ten jeden ] (http://stackoverflow.com/q/3770711/146792): Czy rzeczywiście czytasz pytanie OP do końca? Pytanie jest w pełni poprawne IMO. – mac

+0

@mac: Myślę, że osoba, która głosowała na zamknięcie, już znalazła swój błąd - przynajmniej usunęła automatycznie wygenerowany komentarz. :) –

Odpowiedz

2

Masz dwie opcje:

  1. sondowanie time.time() lub podobne, natomiast kod w pytaniu biegnie. Jest to wykonalne tylko wtedy, gdy ten kod jest pod Twoją kontrolą.

  2. Jak wspomniano przez pajton, można napisać rozszerzenie C, aby zadzwonić pod numer systemowy setitimer(). Nie jest to zbyt trudne, ponieważ wystarczy skopiować kod signal.getitimer() i signal.setitimer() ze źródła późniejszych wersji Pythona. Są tylko cienkimi opakowaniami wokół równie nazwanych wywołań systemowych.

    Ta opcja jest możliwa tylko w przypadku korzystania z CPython i środowiska, w którym można używać niestandardowych rozszerzeń języka C.

    Edit: Oto kod skopiowany z signalmodule.c in Python 2.7 (licencji Pythona ma zastosowanie):

    #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); 
    } 
    

    Zapisz ten kod jako itimermodule.c, skompilować go do rozszerzenia C przy użyciu coś jak

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

    Teraz, jeśli masz szczęście, powinieneś być w stanie zaimportować to z Pythona używając

    import itimer 
    

    i zadzwoń pod numer itimer.setitimer().

+0

Absolutnie nieprzetestowane: ale co z uruchomieniem kodu, który ma zostać przycięty, jeśli sygnał pojawia się pod wątkiem i uruchomieniem zegara (odpytywanie 'time.time()') w kodzie głównym? Timer może zabić wątek po osiągnięciu limitu ... [brzydki, ale ... czy to nie zadziałało?] – mac

+0

@mac: Nie, to nie działa. Nie możesz zabijać wątków w Pythonie. –

+0

@sven: kod nie będzie pod moją kontrolą. W grę wchodzi wiele obliczeń i wywoływanie funkcji, a trudno jest sondować w jednym miejscu. –

4

Podnieś alarm z wątku "daemon" cierpliwie czekającego. W poniższym kodzie snoozealarm robi to, co chcesz przez SnoozeAlarm wątku:

#! /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

Dobry pomysł, +1 .. –

+0

To właśnie nazywam inżynierią "odwrotną". +1;) – mac