线程忽略 KeyboardInterrupt 异常
Posted
技术标签:
【中文标题】线程忽略 KeyboardInterrupt 异常【英文标题】:threading ignores KeyboardInterrupt exception 【发布时间】:2011-04-16 20:04:29 【问题描述】:我正在运行这个简单的代码:
import threading, time
class reqthread(threading.Thread):
def run(self):
for i in range(0, 10):
time.sleep(1)
print('.')
try:
thread = reqthread()
thread.start()
except (KeyboardInterrupt, SystemExit):
print('\n! Received keyboard interrupt, quitting threads.\n')
但是当我运行它时,它会打印出来
$ python prova.py
.
.
^C.
.
.
.
.
.
.
.
Exception KeyboardInterrupt in <module 'threading' from '/usr/lib/python2.6/threading.pyc'> ignored
事实上python线程忽略了我的Ctrl+C键盘中断并且不打印Received Keyboard Interrupt
。为什么?这段代码有什么问题?
【问题讨论】:
【参考方案1】:试试
try:
thread=reqthread()
thread.daemon=True
thread.start()
while True: time.sleep(100)
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
没有调用time.sleep
,主进程太早跳出try...except
块,所以KeyboardInterrupt
没有被捕获。我的第一个想法是使用thread.join
,但这似乎会阻塞主进程(忽略键盘中断),直到thread
完成。
thread.daemon=True
导致线程在主进程结束时终止。
【讨论】:
我相信join
的超时,即while thread.isAlive: thread.join(5)
也将有助于保持主线程对异常的响应。
thread.daemon = True
实际上并不推荐,因为它不允许线程清理任何遗留的资源...【参考方案2】:
总结thecomments中推荐的更改,以下对我很有效:
try:
thread = reqthread()
thread.start()
while thread.isAlive():
thread.join(1) # not sure if there is an appreciable cost to this.
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
sys.exit()
【讨论】:
在“while thread.isAlive():”中一遍又一遍地调用“thread.join()”是一件好事/这有关系吗? 我个人不知道;如果性能对您来说很重要,可能值得尝试制定基准? 注意 exit() 和 sys.exit() 是不一样的。推荐使用 sys.exit()。 感谢@DevPlayer!更新以反映这一点。好奇的可以看***.com/a/19747562/1048433的解释 对我不起作用。它打印Received keyboard interrupt, quitting threads.
,但线程继续打印点。【参考方案3】:
我的(hacky)解决方案是像这样对Thread.join()
进行猴子补丁:
def initThreadJoinHack():
import threading, thread
mainThread = threading.currentThread()
assert isinstance(mainThread, threading._MainThread)
mainThreadId = thread.get_ident()
join_orig = threading.Thread.join
def join_hacked(threadObj, timeout=None):
"""
:type threadObj: threading.Thread
:type timeout: float|None
"""
if timeout is None and thread.get_ident() == mainThreadId:
# This is a HACK for Thread.join() if we are in the main thread.
# In that case, a Thread.join(timeout=None) would hang and even not respond to signals
# because signals will get delivered to other threads and Python would forward
# them for delayed handling to the main thread which hangs.
# See CPython signalmodule.c.
# Currently the best solution I can think of:
while threadObj.isAlive():
join_orig(threadObj, timeout=0.1)
else:
# In all other cases, we can use the original.
join_orig(threadObj, timeout=timeout)
threading.Thread.join = join_hacked
【讨论】:
【参考方案4】:对ubuntu的解决方案稍作修改。
按照 Eric 的建议删除tread.daemon = True 并用 signal.pause() 替换睡眠循环:
import signal
try:
thread=reqthread()
thread.start()
signal.pause() # instead of: while True: time.sleep(100)
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
【讨论】:
不错 - 但不幸的是,Windows 不支持【参考方案5】:将try ... except
放入每个线程中,并将signal.pause()
放入true main()
对我有用。
请注意import lock。我猜这就是 Python 默认不解决 ctrl-C 的原因。
【讨论】:
以上是关于线程忽略 KeyboardInterrupt 异常的主要内容,如果未能解决你的问题,请参考以下文章
使用 KeyboardInterrupt 异常捕获 SIGINT 在终端中有效,而不是在脚本中
Python 3 - 主线程未检测到后台线程中的KeyboardInterrupt,直到用户将鼠标悬停在GUI窗口上
python升级带来的yum异常(解决错误File "/usr/bin/yum", line 30 except KeyboardInterrupt, e:)