我见过很多与此相关的问题。。。但我的代码在python2.6.2上工作,无法在python2.6.5上工作。当程序被一个信号杀死时,通过这个模块注册的整个atexit“函数不会被调用”这种想法是错误的吗?因为我捕捉到信号,然后干净地退出?这是怎么回事?正确的方法是什么?在
import atexit, sys, signal, time, threading
terminate = False
threads = []
def test_loop():
while True:
if terminate:
print('stopping thread')
break
else:
print('looping')
time.sleep(1)
@atexit.register
def shutdown():
global terminate
print('shutdown detected')
terminate = True
for thread in threads:
thread.join()
def close_handler(signum, frame):
print('caught signal')
sys.exit(0)
def run():
global threads
thread = threading.Thread(target=test_loop)
thread.start()
threads.append(thread)
while True:
time.sleep(2)
print('main')
signal.signal(signal.SIGINT, close_handler)
if __name__ == "__main__":
run()
python 2.6.2:
^{pr2}$python 2.6.5:
$ python halp.py
looping
looping
looping
main
looping
looping
main
looping
looping
main
^Ccaught signal
looping
looping
looping
looping
...
looping
looping
Killed <- kill -9 process at this point
2.6.5上的主线程似乎从不执行atexit函数。在
由于信号而退出与从信号处理程序中退出不同。捕捉到信号并用系统出口是一个干净的出口,而不是一个信号处理程序的出口。所以,是的,我同意至少在原则上应该在这里运行atexit处理程序。在
然而,信号处理程序有一个棘手的问题:它们是完全异步的。它们可以随时中断程序流,在任何VM操作码之间。以这个代码为例。(请将此格式与上面的代码相同;为了简洁起见,我省略了代码。)
这里有一个严重的问题:当run()持有
lock
时,可能会收到一个信号(例如^C)。如果发生这种情况,您的信号处理程序将在锁仍然保持的情况下运行。然后,它将等待test\u循环退出,如果该线程正在等待锁,您将死锁。在这是一个完整的问题类别,这就是为什么许多api不从信号处理程序中调用它们。相反,您应该设置一个标志,告诉主线程在适当的时候关闭。在
^{2}$我倾向于避免退出程序系统出口完全和显式地在主出口点(例如run()的末尾)进行清理,但是如果需要,可以在这里使用atexit。在
我不确定这是否完全改变了,但这就是我在2.6.5中实现atexit的方式
这里的根差异实际上与signals和atexit无关,而是
sys.exit
行为的改变。在在大约2.6.5之前,
sys.exit
(更准确地说,在顶层捕捉到SystemExit)将导致解释器退出;如果线程仍然在运行,它们将被终止,就像POSIX线程一样。在在2.6.5左右,行为发生了变化:
sys.exit
的效果现在基本上与从程序的主函数返回相同。当您在两个版本中执行操作时,解释器会在退出之前等待所有线程加入。在相关的变化是
Py_Finalize
现在在靠近顶部的地方调用wait_for_thread_shutdown()
,而以前没有调用。在这种行为改变似乎是不正确的,主要是因为它不再像文档中描述的那样起作用,简单地说:“Exit from Python”。实际效果不再是退出Python,而是简单地退出线程。(附带说明一下,
sys.exit
从另一个线程调用时从未退出过Python,但与记录的行为的模糊差异并不能证明更大的行为是合理的。)我可以看到新行为的吸引力:退出主线程的两种方法(“退出并等待线程”和“立即退出”),只有一种方法,如系统出口基本上等同于从顶部函数返回。然而,这是一个突破性的变化,并偏离了记录在案的行为,这远远超过了这一点。在
由于这种变化,在从上面的信号处理程序
sys.exit
之后,解释器会坐在那里等待线程退出,然后在线程退出之后运行atexit
处理程序。由于处理程序本身告诉线程,所以它本身就是死锁。在相关问题 更多 >
编程相关推荐