Python脚本在循环中卡住
在我的一个脚本中,我使用了一个无限循环来检查是否有活跃的互联网连接:
def online_check():
try:
con = urllib2.urlopen("http://www.google.com/")
data = con.read()
logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]')))
except:
logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]')))
time.sleep(3)
online_check()
我知道这个方法并不是特别优雅,但问题是当我启动脚本时,有时候它会调用 online_check 方法,然后就卡在那儿(大约每200次尝试中会出现一次)。脚本仍然在运行,没有抛出任何异常;就是卡住了。我可以按 CTRL+C(即使脚本卡了几个小时)它也会抛出一个异常,然后继续进行下一个 online_check。我还重写了脚本,改为检查 'ifconfig' 中的 IP 地址,而不是 ping 谷歌,但结果也差不多。
我哪里做错了?我能否重写脚本,让这种情况不再发生?有没有什么方法可以找出这里发生了什么?
非常感谢你的帮助。顺便说一下,我使用的是 Python2.7.1,并且我在 Linux 和 Mac 上都试过这个脚本。
附言:如果你有关于设计一个不消耗带宽且开销最小的连接检查方法的建议,我会非常乐意听取。
4 个回答
除了其他人的建议,你真的应该明确你要捕捉哪些异常。按下控制键C并不能解决问题,因为这只是引发了一个异常,而你的 except
语句会把它当作和其他异常一样处理。
看起来你想要捕捉的异常是 urllib2.URLError
。
你遇到了无限递归的问题。为了避免这种情况,可以给online_check这个方法传递一个参数,用来设置最大检查次数。
比如:
def online_check(max_checks=10, current_check=0):
try:
con = urllib2.urlopen("http://www.google.com/")
data = con.read()
logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]')))
except:
logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]')))
if max_checks > current_check:
time.sleep(3)
online_check(max_checks, current_check+1)
所以你可以这样做:
online_check(5) # for 5 maximum checks
online_check() # use the default value, which is 10
我还建议你捕获更具体的异常,这样可以养成更好的编程习惯。而且,当你按下CTRL-C时,Python会抛出一个KeyboardInterrupt异常,而你的代码实际上会捕获到这个异常,因为你捕获了所有的异常。
除了无限递归的问题(据我所知,CPython不支持优化的尾递归),你还没有关闭你的连接。
你可能会遇到某种连接限制,这可能是操作系统的限制,也可能是谷歌的限制,尤其是在重试的时候。这种情况可能在达到最大递归深度之前就发生了,所以你没有看到异常。
通常情况下,连接会在被垃圾回收时关闭,但由于递归的原因,这个变量在递归结束之前并没有完全失去作用域。
还有,正如@senderle提到的,你应该尝试捕获更具体的错误。
试试:
def online_check():
while True:
try:
con = urllib2.urlopen("http://www.google.com/")
data = con.read()
logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]')))
except urllib2.URLError:
logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]')))
time.sleep(3)
finally:
con.close()
(警告:这段代码未经测试)