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


98

これは愚かな質問かもしれませんが、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から終了する必要があると記載されています。このプログラムの出力から、「post thread exit」は決して出力されないことがわかりますが、メインスレッドは、スレッドがexitを呼び出した後も、継続して続行します。

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

回答:


71

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


23

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

Deestanが説明したメソッドとは別に、呼び出すことができますos._exit(アンダースコアに注意してください)。使用する前にクリーンアップ(呼び出し__del__など)を行わないことを確認してください。


2
I / Oをフラッシュしますか?
Lorenzo Belli 2017年

1
os._exit(n): "ステータスnのプロセスを終了します。クリーンアップハンドラーを呼び出したり、stdioバッファーをフラッシュしたりする必要はありません。"
Tim Richardson、

os._exitcurses内で使用すると、コンソールは通常の状態にリセットされないことに注意してください。resetこれを修正するには、Unixシェルで実行する必要があります。
sjngm

22

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

少なくともLinuxでできること:

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

これにより、SIGINTが発生するメインスレッドにが送信されますKeyboardInterrupt。これで、適切なクリーンアップが行われました。必要に応じて信号を処理することもできます。

ところで、WindowsではSIGTERM、Pythonからはキャッチできない信号のみを送信できます。その場合os._exitは、同じ効果で簡単に使用できます。



12

「メイン終了前、スレッド終了後」が出力されているのは、気になることですか?

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

ここにsys.exit(ちょうどprint sys.exit.__doc__)のドキュメントがあります:

SystemExit(status)を発生させてインタープリターを終了します。
ステータスが省略されるかNoneの場合、デフォルトでゼロ(つまり、成功)になります。
ステータスが数値の場合、システムの終了ステータスとして使用されます。
それが別の種類のオブジェクトである場合、それは出力され、システムの
終了ステータスは1(つまり、失敗)になります。

これにはいくつかの影響があります:

  • スレッドでは、プロセス全体ではなく、現在のスレッドを強制終了するだけです(スタックの最上位に到達したと想定します...)
  • オブジェクトデストラクタ(__del__)は、それらのオブジェクトを参照するスタックフレームが展開されるときに呼び出される可能性があります
  • 最後に、スタックがほどけるとブロックが実行されます
  • SystemExit例外をキャッチできます

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


11

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

私が好む方法は、Erlang風のメッセージパッシングです。少し単純化されて、私はこのようにします:

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

3
Queueここは必要ありません。単純なものでbool十分です。この変数のクラシック名はでis_active、その初期デフォルト値はTrueです。
Acumenus 2013

3
はい。それで合っています。effbot.org/zone/thread-synchronization.htmによると、bool(またはその他のアトミック操作)を変更すると、この特定の問題に対して完全に機能します。私は一緒に行く理由Queuesが、ねじの薬剤を扱うとき、私はいくつかの異なる信号を必要としてしまう傾向があることである(flushreconnectexit、など...)ほとんどすぐ。
Deestan 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.