我写过一个网络抓取工具,我希望能够通过键盘停下来。当我打断它时,我不希望程序死掉;它需要先将其数据刷新到磁盘。我也不想捕获KeyboardInterruptedException
,因为持久数据可能处于不一致状态。
我目前的解决方案是定义一个捕获SIGINT
并设置标志的信号处理程序;主循环的每次迭代都会在处理下一个URL之前检查此标志。
但是,我发现如果系统在发送中断时恰好执行socket.recv()
,我会得到这个:
^C
Interrupted; stopping... // indicates my interrupt handler ran
Traceback (most recent call last):
File "crawler_test.py", line 154, in <module>
main()
...
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 397, in readline
data = recv(1)
socket.error: [Errno 4] Interrupted system call
并且该过程完全退出。为什么会这样?有没有办法可以阻止中断影响系统调用?
答案 0 :(得分:8)
socket.recv()
调用C层中与POSIX兼容的基础recv
函数,当进程收到EINTR
时,该函数将返回错误代码SIGINT
等待recv()
中的传入数据。此错误代码可以在C端使用(如果您使用C编程),以检测recv()
返回的错误,因为套接字上有更多可用数据,但因为进程收到SIGINT
。无论如何,这个错误代码被Python变成了一个异常,并且由于它从未被捕获,它会使用你看到的回溯来终止你的应用程序。解决方案只是捕获socket.error
,检查错误代码,如果它等于errno.EINTR
,则静默忽略该异常。像这样:
import errno
try:
# do something
result = conn.recv(bufsize)
except socket.error as (code, msg):
if code != errno.EINTR:
raise
答案 1 :(得分:3)
如果您不想中断套接字调用,请在设置信号处理程序后禁用中断行为。
signal.signal(<your signal here>, <your signal handler function here>)
signal.siginterrupt(<your signal here>, False)
在信号处理功能中设置一些标志,例如一个threading.Event()然后在主处理函数中检查该标志并正常终止你的爬虫。
背景信息: