web-dev-qa-db-fra.com

потоки игнорируют исключение KeyboardInterrupt

Запускаю этот простой код:

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

44
Emilio

Essayer 

try:
  thread=reqthread()
  thread.daemon=True
  thread.start()
  while True: time.sleep(100)
except (KeyboardInterrupt, SystemExit):
  print '\n! Received keyboard interrupt, quitting threads.\n'

Sans l'appel à time.sleep, le processus principal quitte trop tôt le bloc try...except, de sorte que la variable KeyboardInterrupt n'est pas interceptée. Ma première pensée a été d'utiliser thread.join, mais cela semble bloquer le processus principal (en ignorant KeyboardInterrupt) jusqu'à ce que thread soit terminé.

thread.daemon=True entraîne la fin du thread à la fin du processus principal.

57
unutbu

Pour résumer les modifications recommandées dans lescommentaires , les éléments suivants me conviennent: 

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()
9
rattray

Légère modification de la solution d'ubuntu.

Suppression de tread.daemon = True comme suggéré par Eric et remplacement de la boucle en veille par 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'
3
yaccob

Mettre le try ... except dans chaque thread et une signal.pause() dans truemain() fonctionne pour moi.

Méfiez-vous des import lock quoique. Je suppose que c’est la raison pour laquelle Python ne résout pas ctrl-C par défaut.

0
personal_cloud

Ma solution (hacky) consiste à monkey-patch Thread.join() ainsi:

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
0
Albert