【问题标题】:threading ignores KeyboardInterrupt exception线程忽略 KeyboardInterrupt 异常
【发布时间】: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。为什么?这段代码有什么问题?

【问题讨论】:

    标签: python multithreading events exception keyboardinterrupt


    【解决方案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!更新以反映这一点。好奇的可以看stackoverflow.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 的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-11
        • 2011-03-13
        • 1970-01-01
        • 2022-06-16
        • 2019-06-03
        相关资源
        最近更新 更多