web-dev-qa-db-ja.com

Ctrl-C、つまりKeyboardInterruptでPython

KeyboardInterrupt例外はPythonのメインスレッドでのみ発生することをどこかで読みました。子スレッドが実行されている間、メインスレッドがブロックされることも読みました。つまり、これは CTRL+C 子スレッドに到達することはできません。私は次のコードを試しました:

def main():
    try:
        thread = threading.Thread(target=f)
        thread.start()  # thread is totally blocking (e.g., while True)
        thread.join()
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

この場合、影響はありません CTRL+C 実行時。信号が聞こえないようです。私はこれを間違った方法で理解していますか?使用してスレッドを殺す他の方法はありますか CTRL+C

22
Amit S

問題は、あなたがthread1.join()を使用していることです。これにより、プログラムは、そのスレッドが継続して終了するまで待機します。

シグナルを受け取るのはシグナルであり、スレッドを持つのはプロセスなので、シグナルは常にメインプロセスによってキャッチされます。

あなたが示すようにそれを行うと、あなたは1つのスレッドを開始し、それが継続するのが終了するまで待つので、基本的にはスレッド機能のない「通常の」アプリケーションを実行しています。

10
webbi

あなたが受け取るメインスレッドを持ちたいなら CTRL+C 参加中にシグナルを送信するには、join()呼び出しにタイムアウトを追加することで実行できます。

次のコードは機能しているようです(mainを実際に終了させたい場合は、daemon=Trueを忘れずに追加してください)。

thread1.start()
while True:
    thread1.join(600)
    if not thread1.isAlive():
        break
13
korc

Pythonでは、各プロセスのメインスレッドでのみKeyboardInterrupt例外が発生することは事実です。しかし、他の回答が述べたように、メソッド_Thread.join_が呼び出しスレッドをブロックすることも真実ですincludeingKeyboardInterrupt例外。それが理由です Ctrl+C メインスレッドでの実行はthread.join()の行でブロックされたままです。

したがって、あなたの質問に対する簡単な解決策は、最初にtimeout引数thread.join()に追加し、その呼び出しをループで終了することです子スレッドが終了すると、タイムアウトごとにKeyboardInterrupt例外が発生します。次に、子スレッドを daemonic にします。これは、親(ここではメインスレッド)が終了時に強制終了します(非デーモンスレッドのみが強制終了されず、親が終了するときに参加します)。

_def main():
    try:
        thread = threading.Thread(target=f, daemon=True)  # create a daemon child thread
        thread.start()

        while thread.is_alive():
            thread.join(1)  # join shortly to not block KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work
_

しかし、子スレッドのコードを制御する場合、より良い解決策は、たとえば_threading.Event_を使用して、(最初の解決策のように突然ではなく)正常に終了するように子スレッドに通知することです。

_def main():
    try:
        event = threading.Event()
        thread = threading.Thread(target=f, args=(event,))
        thread.start()
        event.wait()  # wait forever but without blocking KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        event.set()  # inform the child thread that it should exit
        sys.exit(1)

def f(event):
    while not event.is_set():
        pass  # do the actual work
_
3
Maggyero