线程忽略 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:)

线程任务异常终止问题

异常

异常