потоки игнорируют исключение KeyboardInterrupt - PullRequest
44 голосов
/ 24 сентября 2010

Я использую этот простой код:

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. Зачем? Что не так с этим кодом?

Ответы [ 5 ]

58 голосов
/ 24 сентября 2010

Попробуйте

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, но, похоже, это блокирует основной процесс (игнорируя KeyboardInterrupt) до тех пор, пока не завершится thread.

thread.daemon=True заставит поток завершиться, когда завершится основной процесс.

10 голосов
/ 01 мая 2014

Подводя итог изменениям, рекомендованным в комментариях , мне хорошо подходит следующее:

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()
4 голосов
/ 26 февраля 2016

Небольшая модификация решения Ubuntu.

Удаление 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'
0 голосов
/ 22 сентября 2017

Помещение try ... except в каждый поток, а также signal.pause() in true main() работает для меня.

Остерегайтесь импорт блокировки хотя,Я предполагаю, что именно поэтому Python не решает Ctrl-C по умолчанию.

0 голосов
/ 06 марта 2015

Мое (хакерское) решение состоит в том, чтобы обезьяньим патчем 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
...