web-dev-qa-db-ja.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。どうして?このコードの何が問題になっていますか?

45
Emilio

試してみる

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が終了するまでメインプロセスを(KeyboardInterruptを無視して)ブロックするようです。

thread.daemon=Trueは、メインプロセスの終了時にスレッドを終了させます。

58
unutbu

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

Ubuntuのソリューションのわずかな変更。

Ericが示唆するようにtread.daemon = Trueを削除し、sleep.loopを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'
4
yaccob

_try ... except_を各スレッドに入れ、さらにsignal.pause()truemain()に入れるとうまくいきます。

ただし、 import lock に注意してください。これが、Pythonがデフォルトでctrl-Cを解決しない理由です。

0
personal_cloud

私の(ハックな)解決策は、次のようにモンキーパッチ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
0
Albert