如何在Python 2.4中0.5秒后发出警报
我想让一段特定的Python代码在运行0.5秒后超时。所以我打算在0.5秒后抛出一个异常或信号,然后优雅地处理这个异常,继续执行后面的代码。
在Python中,我知道可以用signal.alarm()
来设置一个整数秒的闹钟。有没有其他方法可以在0.5秒后产生一个闹钟呢?在其他帖子中提到的signal.setitimer()
在Python 2.4中不可用,而我需要使用Python 2.4来实现这个目的。
2 个回答
2
你有两个选择:
可以在相关代码运行时,不停地检查
time.time()
或类似的函数。这种方法显然只有在你能控制那段代码的时候才行。正如pajton提到的,你可以写一个C语言扩展,来调用系统函数
setitimer()
。这并不难,因为你可以直接复制后续版本Python中signal.getitimer()
和signal.setitimer()
的代码。它们其实只是对同名系统调用的简单封装。这个选择只有在你使用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()
。
5
从一个耐心等待的“守护进程”线程中发出警报。在下面的代码中,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()