web-dev-qa-db-ja.com

Pythonのスレッド内で呼び出されたときにsys.exit()が終了しないのはなぜですか?

これはばかげた質問かもしれませんが、Pythonについての私の仮定のいくつかをテストしており、スレッドで呼び出されたときに次のコードスニペットが終了しない理由について混乱していますが、メインスレッドで呼び出されると終了します。

import sys, time
from threading import Thread

def testexit():
    time.sleep(5)
    sys.exit()
    print "post thread exit"

t = Thread(target = testexit)
t.start()
t.join()
print "pre main exit, post thread exit"
sys.exit()
print "post main exit"

Sys.exit()のドキュメントには、呼び出しはPythonから終了する必要があると記載されています。このプログラムの出力から、「スレッド終了後」は決して出力されないことがわかりますが、メインスレッドは、スレッドがexitを呼び出した後も継続します。

スレッドごとにインタープリターの個別のインスタンスが作成されており、exit()の呼び出しがその個別のインスタンスを終了しているだけですか?もしそうなら、スレッド実装は共有リソースへのアクセスをどのように管理しますか?スレッドからプログラムを終了したい場合はどうすればよいですか(実際にしたいのではなく、理解しただけです)。

76
Shabbyrobe

sys.exit()は、thread.exit()と同様にSystemExit例外を発生させます。したがって、sys.exit()がそのスレッド内でその例外を発生させると、thread.exit()を呼び出すのと同じ効果があります。そのため、スレッドのみが終了します。

59
rpkelly

スレッドからプログラムを終了したい場合はどうなりますか?

説明したメソッドDeestanとは別に、os._exit(アンダースコアに注意してください)。使用する前に、それがno cleanups(__del__ または類似)。

20
Helmut Grohne

スレッドからプログラムを終了したい場合はどうすればよいですか(実際にしたいのではなく、理解しただけです)。

少なくともLinuxでは、次のようなことができます。

os.kill(os.getpid(), signal.SIGINT)

これにより、SIGINTがメインスレッドに送信され、KeyboardInterruptとして処理できます。

ところで:Windowsでは、SIGTERMシグナルのみを送信できますが、Pythonからはキャッチできません。 (私はHelmutが示唆するos._exitを呼び出すことを好むでしょう)

15
Chris

「メイン終了前、スレッド終了後」が表示されているという事実は、あなたを悩ますものですか?

sys.exit(Javaの場合はSystem.exitに類似)がVM /プロセス/インタープリターをすぐに停止させる他の言語(Javaなど)とは異なり、Pythonのsys.exitは例外をスローします。特にSystemExit例外。

sys.exit(ちょうどprint sys.exit.__doc__)のドキュメントは次のとおりです。

SystemExit(status)を上げてインタープリターを終了します。
ステータスが省略された場合、またはなしの場合、デフォルトはゼロ(つまり成功)になります。
ステータスが数値の場合、システム終了ステータスとして使用されます。
別の種類のオブジェクトの場合、印刷され、システム
終了ステータスは1(つまり、失敗)になります。

これにはいくつかの結果があります。

  • スレッドでは、プロセス全体ではなく、現在のスレッドを強制終了します(スタックの一番上に到達すると仮定します...)
  • オブジェクトデストラクタ(__del__)は、それらのオブジェクトを参照するスタックフレームが巻き戻されると潜在的に呼び出されます
  • スタックが巻き戻されると最終的にブロックが実行されます
  • SystemExit例外をキャッチできます

最後はおそらく最も驚くべきことであり、あなたのPythonコードに未修飾のexceptステートメントがほとんど決してないはずであるもう一つの理由です。

11

スレッドからプログラムを終了したい場合はどうすればよいですか(実際にしたいのではなく、理解しただけです)。

私が好む方法は、アーランっぽいメッセージの受け渡しです。少し簡略化して、次のようにします。

import sys, time
import threading
import Queue # thread-safe

class CleanExit:
  pass

ipq = Queue.Queue()

def testexit(ipq):
  time.sleep(5)
  ipq.put(CleanExit)
  return

threading.Thread(target=testexit, args=(ipq,)).start()
while True:
  print "Working..."
  time.sleep(1)
  try:
    if ipq.get_nowait() == CleanExit:
      sys.exit()
  except Queue.Empty:
    pass
11
Deestan