スレッド/プロセスが強制終了された場合に実行が突然停止することは理にかなっています
ターミナル ウィンドウで [X] をクリックしてメイン プログラムを正常に終了すると、クリーンアップ コードが実行されないのはなぜですか?
私はまだマルチスレッド アプリケーションの詳細を学んでいます。私の問題は、Python がバックグラウンド スレッドの強制終了を処理する方法を理解していないことが原因だと思います。
質問:
finally:
ブロックが常に実行されないのはなぜですか?- 他に
finally:
ブロックが実行されないのはいつですか? - スレッドが強制終了されると、スレッド内のコード実行はどうなりますか?
- メイン プロセスを終了すると、デーモン/非デーモン スレッドはどうなりますか?
詳細:
ZMQ ソケットを使用して (とりわけ) ログ ファイルに内容を書き込むマルチスレッド プログラムを作成しようとしています。ロギング スレッドが終了する直前に無条件にメッセージとクリーンアップを実行するようにしたいのですが、ほとんどの場合はそうではありません。
以下の関数は、バックグラウンド スレッドで無限ループを開始し、zmq.PAIR
通信用のソケットを返します。それが開始するループはソケットをリッスンし、そのソケットに書き込まれたものはすべてファイルに書き込まれます。ループはまた、「I'm started to log now!」、「おっと、エラーが発生しました!」などの診断メッセージを送り返す(はずです)。「私は今出ています」。そのため、メインプログラムはそれを監視できます。
プログラムは、このmain
パターンを使用していくつかのスレッドを生成し、さまざまな部分を監視/制御します。いくつかの ZMQ ソケット (STDIN とシリアル ポートに接続) でメッセージをポーリングし、その一部をファイルに接続されたソケットに転送します。
しかし今、私は立ち往生しています。プログラムのmain
ルーティングと制御ロジックは正常に動作します。get_logfile_sock
のファイル書き込みは正常に機能し、通常の例外処理は期待どおりに機能します。しかし、スレッドがメイン プログラムから強制終了された場合、またはメイン プログラムを完全に停止した場合、「I'm exiting now」コードは実行されません。
例:
def get_logfile_sock(context, file_name):
"""
Returns a ZMQ socket. Anything written to the socket gets appended to the a specified file. The socket will send diagnostic messages about file opening/closing and any exceptions encountered.
"""
def log_file_loop(socket):
"""
Read characters from `socket` and write them to a file. Send back diagnostic and exception information.
"""
try:
socket.send("Starting Log File {}".format(file_name))
with open(file_name, "a+") as fh:
# File must start with a timestamp of when it was opened
fh.write('[{}]'.format(get_timestamp()))
# Write all strings/bytes to the file
while True:
message = socket.recv()
fh.write(message)
fh.flush()
# Un-comment this line to demonstrate that the except: and finally: blocks both get executed when there's an error in the loop
# raise SystemExit
except Exception as e:
# This works fine when/if there's an exception in the loop
socket.send("::".join(['FATALERROR', e.__class__.__name__, e.message]))
finally:
# This works fine if there's an exception raised in the loop
# Why doesn't this get executed when my program exits? Isn't that just the main program raising SystemExit?
# Additional cleanup code goes here
socket.send("Closing socket to log file {}".format(file_name))
socket.close()
# Make a socket pair for communication with the loop thread
basename = os.path.basename(file_name).replace(":", "").replace(" ", "_").replace(".", "")
SOCKNAME = 'inproc://logfile-{}'.format(basename)
writer = context.socket(zmq.PAIR)
reader = context.socket(zmq.PAIR)
writer.bind(SOCKNAME)
reader.connect(SOCKNAME)
# Start the loop function in a separate thread
thread = threading.Thread(target=log_file_loop, args=[writer])
thread.daemon = True # is this the right thing to do?
thread.start()
# Return a socket endpoint to the thread
return reader