Ctrl-CでPythonスクリプトを強制終了できない


117

次のスクリプトでPythonスレッドをテストしています。

import threading

class FirstThread (threading.Thread):
    def run (self):
        while True:
            print 'first'

class SecondThread (threading.Thread):
    def run (self):
        while True:
            print 'second'

FirstThread().start()
SecondThread().start()

これはKubuntu 11.10のPython 2.7で実行されています。Ctrl+ Cそれを殺すことはありません。また、システムシグナルのハンドラーを追加しようとしましたが、それは役に立ちませんでした:

import signal 
import sys
def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

プロセスを強制終了するには、Ctrl+ Zでプログラムをバックグラウンドに送信した後、PIDで強制終了します。これは無視されません。なぜCtrl+ Cはそんなに永続的に無視されるのですか?どうすればこれを解決できますか?


@dotancohenはWindowsで動作していますか?
キリロフ

@vitaibian:Windowsではテストしていませんが、OS固有ではないようです。
dotancohen 2012

回答:


178

Ctrl+ Cはメインスレッドを終了しますが、スレッドはデーモンモードではないため、実行を継続し、プロセスを存続させます。それらをデーモンにすることができます:

f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()

ただし、別の問題があります。メインスレッドがスレッドを開始すると、それ以外に何も実行できません。そのため、終了し、スレッドは即座に破棄されます。メインスレッドを存続させましょう。

import time
while True:
    time.sleep(1)

これで、Ctrl+ を押すまで、「最初」と「2番目」が印刷され続けますC

編集:コメント投稿者が指摘したように、デーモンスレッドは一時ファイルなどをクリーンアップする機会を得られない可能性があります。それが必要な場合KeyboardInterruptは、メインスレッドでをキャッチし、クリーンアップとシャットダウンを調整します。しかし、多くの場合、デーモンスレッドを突然停止させるだけで十分でしょう。


6
このスレッドを実行しても正常に停止せず、一部のリソースが解放されないことを言及する必要があります。
Tommaso Barbugli 2013年

1
まあ、Ctrl-Cで何かを停止する方法はありません。残っているリソースがわからない-プロセスが終了したときにOSが何かを再利用してはいけませんか?
トーマスK

7
@ThomasKによって作成された一時ファイルtempfile.TemporaryFile()は、たとえばディスク上に残される場合があります。
Feuermurmel 2013

1
@ deed02392:メインスレッドがどうなるか正確にはわかりませんが、メインスレッドが終了すると、メインスレッドで何も実行できなくなります。デーモン以外のスレッドがすべて終了すると、プロセスが終了します。親子関係はそこに入りません。
トーマスK

4
python3でのように見えるあなたが渡すことができますdaemon=TrueThread.__init__
ライアン海寧


3

スレッドが死ぬことが予想される場合は、スレッドでjoin()を呼び出すのが最善だと思います。ループを終了させるために、コードを少し自由にしました(必要なクリーンアップの必要性を追加することもできます)。変数dieは、各パスで真かどうかがチェックされ、真の場合、プログラムが終了します。

import threading
import time

class MyThread (threading.Thread):
    die = False
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run (self):
        while not self.die:
            time.sleep(1)
            print (self.name)

    def join(self):
        self.die = True
        super().join()

if __name__ == '__main__':
    f = MyThread('first')
    f.start()
    s = MyThread('second')
    s.start()
    try:
        while True:
            time.sleep(2)
    except KeyboardInterrupt:
        f.join()
        s.join()

while Trueばかげているので、あなたはjoin直接すべきです-そして、オーバーライドされた関数は一種の疑わしいものです。多分def join(self, force=False): if force: self.die = Trueそれjoin()join(force=True)彼らを殺すことによって変更されていません。しかし、それでも、どちらか一方に参加する前に両方のスレッドに通知する方が良いでしょう。
o11c

0

@Thomas Kの回答の改良版:

  • この要点is_any_thread_alive()に従ってアシスタント機能を定義すると、自動的に終了できます。main()

コード例:

import threading

def job1():
    ...

def job2():
    ...

def is_any_thread_alive(threads):
    return True in [t.is_alive() for t in threads]

if __name__ == "__main__":
    ...
    t1 = threading.Thread(target=job1,daemon=True)
    t2 = threading.Thread(target=job2,daemon=True)
    t1.start()
    t2.start()

    while is_any_thread_alive([t1,t2]):
        time.sleep(0)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.