Как подать сигнал тревоги в python 2.4 через 0,5 секунды

Я хочу прервать определенный фрагмент кода Python после запуска в течение 0,5 секунды. Поэтому я намерен вызвать исключение/сигнал через 0,5 секунды, изящно обработать его и продолжить остальную часть кода.

В питоне я знаю, что signal.alarm() может устанавливать будильник на целое число секунд. Есть ли альтернатива, где мы можем сгенерировать сигнал тревоги через 0,5 секунды. signal.setitimer(), как предлагается в других сообщениях, недоступен в python2.4, и мне нужно использовать python2.4 для этой цели?


person Nithin Lingala    schedule 23.11.2011    source источник
comment
написание расширения C - один из вариантов...   -  person pajton    schedule 23.11.2011
comment
Для тех, кто проголосовал за закрытие этого вопроса, поддерживающего эту идею, является точной копией этого: действительно ли вы читали вопрос OP до все кончено? Вопрос полностью действителен ИМО.   -  person mac    schedule 23.11.2011
comment
@mac: Я думаю, что человек, который проголосовал за закрытие, уже понял свою ошибку - по крайней мере, она удалила автоматически сгенерированный комментарий. :)   -  person Sven Marnach    schedule 23.11.2011


Ответы (2)


Поднимите тревогу из терпеливо ожидающего потока «демона». В приведенном ниже коде snoozealarm делает то, что вы хотите, через поток 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()
person pilcrow    schedule 23.11.2011
comment
Это то, что я называю реверс-инжинирингом. +1 ;) - person mac; 24.11.2011

У вас есть два варианта:

  1. Опрос time.time() или аналогичный во время выполнения рассматриваемого кода. Очевидно, что это возможно только в том случае, если этот код находится под вашим контролем.

  2. Как уже упоминал Пайтон, вы можете написать расширение C для вызова системного вызова setitimer(). Это не так уж сложно, потому что вы можете просто скопировать код signal.getitimer() и signal.setitimer() из исходного кода более поздних версий Python. Это всего лишь тонкие оболочки вокруг системных вызовов с одинаковыми именами.

    Этот вариант возможен только в том случае, если вы используете CPython и находитесь в среде, которая позволяет использовать пользовательские расширения C.

    Изменить: вот код, скопированный из signalmodule.c в Python 2.7 (применяется лицензия Python):

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

    Сохраните этот код как itimermodule.c, скомпилируйте его в расширение C, используя что-то вроде

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

    Теперь, если вам повезет, вы сможете импортировать это из Python, используя

    import itimer
    

    и позвоните itimer.setitimer().

person Sven Marnach    schedule 23.11.2011
comment
Абсолютно непроверено: но как насчет запуска кода для обрезки, если сигнал возникает в потоке, и запуска таймера (опрос time.time()) в основном коде? Таймер может убить поток, как только будет достигнут предел... [уродливо, но... разве это не сработает?] - person mac; 23.11.2011
comment
@mac: Нет, это не работает. Вы не можете убивать потоки в Python. - person Sven Marnach; 23.11.2011
comment
@sven: код не будет под моим контролем. Здесь задействовано множество вычислений и вызовов функций, и опрос в одном месте затруднен. - person Nithin Lingala; 23.11.2011
comment
Я редко, если когда-либо, работаю с потоками, поэтому я доверяю вам в этом! Тем не менее, у меня было впечатление, что есть обходной путь (хотя и с ограничениями), чтобы действительно добиться убийства... - person mac; 23.11.2011
comment
Я использую cpython и могу использовать расширения c++ (я использую lib_boost) для определенных вычислений. Поэтому, пожалуйста, предоставьте дополнительную информацию - person Nithin Lingala; 23.11.2011
comment
Создание потоков с соединением (тайм-аут) и их уничтожение для меня нецелесообразно, потому что мне нужно выполнять такие операции по 0,5 секунды очень большое количество раз, и это может сильно испачкаться. - person Nithin Lingala; 23.11.2011
comment
@mac: Спасибо за ссылку. Я знал о большинстве вещей в этой ветке, но в сообщении Bluebird75 было несколько интересных новых деталей. (Мой предыдущий комментарий соответствует ответу Мартина против Лёвиса в связанном вопросе.) Способ Bluebird75 здесь также не подходит, поскольку, насколько я понимаю, код занят в расширении С++. Вызов исключения Python не прерывает код C++. - person Sven Marnach; 23.11.2011
comment
Да, большая часть кода занята в нескольких модулях C++, открытых для python через libboost. Так есть ли другой способ, кроме опроса в разных точках кода? - person Nithin Lingala; 23.11.2011
comment
@NithinLingala: добавлены дополнительные сведения о переносе функции setitimer() на Python 2.4. - person Sven Marnach; 23.11.2011
comment
Я тестировал это только с Python 2.5, так как у меня нет установки Python 2.4. - person Sven Marnach; 23.11.2011
comment
@mac — должно сработать обратное вашему предложению. Поместите тревожную функцию, а не основной код, в фоновый поток. См. мой ответ - person pilcrow; 24.11.2011
comment
@sven, pilcrow: оба решения действительно хороши. Но, как сказал Свен, это будут исключения Python, и они не будут прерывать код C++. Эти решения отвечают на мой первоначальный вопрос, но приносим свои извинения за то, что я не знал, что исключения Python не будут прерывать код C++. - person Nithin Lingala; 24.11.2011
comment
@NithinLingala: Вы правы, я должен был заметить. В этом случае нужно найти место для опроса флага в коде C++, запустить код C++ в потоке (не забудьте освободить GIL на случай, если boost.python не позаботится об этом), пусть основной поток засыпает на полсекунды, а затем поднимает флаг. - person Sven Marnach; 24.11.2011