【问题标题】:How to signal alarm in python 2.4 after 0.5 seconds0.5秒后如何在python 2.4中发出警报
【发布时间】:2012-01-04 18:19:33
【问题描述】:

我想在运行 0.5 秒后让一段特定的 python 代码超时。所以我打算在 0.5 秒后引发异常/信号,并优雅地处理它并继续其余代码。

在 python 中,我知道signal.alarm() 可以设置整数秒的警报。是否有任何替代方法可以在 0.5 秒后生成警报。 signal.setitimer() 在其他帖子中建议在 python2.4 中不可用,我需要为此使用 python2.4 吗?

【问题讨论】:

  • 编写 C 扩展是一种选择...
  • 对于投票结束这个支持这个想法的问题的人是this one 的完全相同的副本:你真的阅读了 OP 问题直到它结束吗?问题是完全有效的 IMO。
  • @mac:我认为投票关闭的人已经发现了她的错误——至少她删除了自动生成的评论。 :)

标签: python timeout signals alarm python-2.4


【解决方案1】:

从耐心等待的“守护进程”线程发出警报。在下面的代码中,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()

【讨论】:

  • 这就是我所说的“逆向”工程。 +1 ;)
【解决方案2】:

你有两个选择:

  1. 在有问题的代码运行时轮询 time.time() 或类似的。这显然只有在您控制该代码时才可行。

  2. 正如pajton 所说,您可以编写一个C 扩展来调用系统调用setitimer()。这并不难,因为您可以简单地从更高版本的 Python 源中复制 signal.getitimer()signal.setitimer() 的代码。它们只是对同名系统调用的薄包装。

    此选项仅在您使用 CPython 并且您所在的环境允许您使用自定义 C 扩展时才可行。

    编辑:这是从signalmodule.c in 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 从 Python 中导入它

    import itimer
    

    并致电itimer.setitimer()

【讨论】:

  • 绝对未经测试:但是如果信号发生在线程下,运行要修剪的代码,并在主代码中运行计时器(轮询time.time())呢?一旦达到限制,计时器可能会终止线程...... [丑陋但......它不能工作吗?]
  • @mac:不,这不起作用。你不能在 Python 中杀死线程。
  • @sven:代码不受我控制。涉及大量计算和函数调用,很难在一个地方轮询。
  • 我很少使用线程,所以我相信你!然而,我在the impression 下有一些解决方法(尽管有限制)来实际实现杀戮......
  • 我正在使用 cpython,并且允许使用 c++ 扩展(我使用 lib_boost)进行某些计算。所以请提供更多细节
猜你喜欢
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-11
  • 2015-09-04
  • 2020-10-12
  • 2020-10-27
相关资源
最近更新 更多