如果线程/进程被杀死,执行突然停止是有意义的
当我通过单击终端窗口上的[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
答案 0 :(得分:2)
线程被杀死时不执行
不要杀死线程。请他们退出,然后join
就可以了。考虑传递Condition
给他们检查。
长答案:执行kill
将导致线程退出而不保证它完成任何特定的块,并且您不应期望之后系统的良好行为。使用multiprocessing
时,这可能会更安全一些。
答案 1 :(得分:0)
最佳做法是创建一个自己的信令层(允许许多事情,包括发送/接收软SigKILL信号)。
这使您的进程间消息传递体系结构"清洁" &安培;完全在你的控制之下。
收到软SigKILL后,您的线程代码可能会处理所有必要的步骤,包括提出你自己的子类型的异常,这在你想要的与异常相关的结构中是有意义的:
try:
# ... primary flow of a <code-block>-execution
if ( SigINPUT == "SigKILL" ):
raise SigKILL_EXCEPTION
except KeyboardInterrupt:
# ... handle KeyboardInterrupt
except MemoryError:
# ... handle MemoryError
except NotImplemented:
# ... handle NotImplemented
except SigKILL_EXCEPTION:
# ... handle SigKILL_EXCEPTION
# situation-specific <code-block> shall rather be here, than in "finally:"
# /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
except:
# ... handle *EXC
finally:
# +++ ALWAYS DO THIS |||||||||||||||||||||||||||||||||||||||||||||||||||||
#
# ... a common <code-block> is ALWAYS executed, under all circumstances
# -> put an attempt to RETURN into SigKILL_EXCEPTION section a test this
# +++ ALWAYS DO THIS |||||||||||||||||||||||||||||||||||||||||||||||||||||
def testTryFinally():
try:
print "TRY:" # show
raise KeyboardInterrupt # used to simulate SigKILL
except KeyboardInterrupt: # EXC. to handle SigKILL ( emulated by KBDI )
print "EXC.KBDI/SigKILL" # show
print "EXC.KBDI:Going to RET(SigKILL)" # remind the next instr. RET!!
return "EXC.KBDI:RET(SigKILL)" # execute RET <value1>
except: # EXC. collects all unhandled EXC-s
print "EXC.*" # show
finally: # FINALLY: clause
print "FINALLY: entered" # show
return "RET(End)" # execute RET <value2>
>>> testTryFinally()
TRY:
EXC.KBDI/SigKILL
EXC.KBDI:Going to RET
FINALLY: entered
EXC.KBDI:RET(SigKILL)
为了处理窗口框架右上角的[X] -window-frame-icon的点击,Tkinter提供了一个很好的解决方案。有一个人可以指定这个事件由一个专门的代码(anEventHANDLER)来处理,这个代码仍然可以在这样的kill-kiss中存活下来并负责任地执行所有脏东西(包括注意优雅地释放所有资源),然后在外部终止进程终止之前通过操作系统。
Syntax:
win.protocol( 'WM_DELETE_WINDOW', lambda:None ) # blocks this way to terminate
win.protocol( 'WM_DELETE_WINDOW', aSendSigKILL_eventHANDLER )
在进程之间创建了一个软信令,允许您控制和分派软SIG,以便允许/强制所有分布式线程获取SIG消息并相应地处理它们自己的执行。