如果线程/进程被杀死,执行突然停止是有意义的
为什么在我的终端窗口点击[X]正常退出主程序时它不会执行清理代码?
我仍在学习多线程应用程序的来龙去脉,我认为我的问题来自于不了解 Python 如何处理杀死后台线程。
问题:
- 为什么我的块不会
finally:
一直执行? - 什么时候不会
finally:
执行块? - 当线程被杀死时,线程内的代码执行会发生什么?
- 当您退出主进程时,守护程序/非守护程序线程会发生什么?
细节:
我正在尝试使用 ZMQ 套接字编写一个多线程程序,该套接字(除其他外)将内容写入日志文件。我希望日志线程在它死之前无条件地执行一些消息传递和清理,但大多数时候不会。
下面的函数在后台线程中启动一个无限循环,并返回一个zmq.PAIR
用于通信的套接字。它开始的循环侦听一个套接字,写入该套接字的任何内容都会写入文件。该循环还(应该)发回诊断消息,例如“我现在开始登录!”,“糟糕,出现错误!” “我现在要退出”。所以主程序可以密切关注它。
该main
程序使用此模式生成一些线程来监视/控制不同的位和部分。它轮询几个 ZMQ 套接字(连接到 STDIN 和串行端口)以获取消息,并将其中的一些转发到连接到文件的套接字。
但现在我被困住了。该main
程序的路由和控制逻辑工作正常。get_logfile_sock
的文件写入工作正常,正常的异常处理按预期工作。但是当线程从主程序中被终止时,或者当我完全停止主程序时,“我现在退出”代码不会执行。
例子:
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