Python2.6.x在某些版本上无法加载/signals/atexit?

2024-04-27 09:51:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我见过很多与此相关的问题。。。但我的代码在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函数。在


Tags: 函数truesignaltime信号maindefsys
3条回答

由于信号而退出与从信号处理程序中退出不同。捕捉到信号并用系统出口是一个干净的出口,而不是一个信号处理程序的出口。所以,是的,我同意至少在原则上应该在这里运行atexit处理程序。在

然而,信号处理程序有一个棘手的问题:它们是完全异步的。它们可以随时中断程序流,在任何VM操作码之间。以这个代码为例。(请将此格式与上面的代码相同;为了简洁起见,我省略了代码。)

import threading
lock = threading.Lock()
def test_loop():
    while not terminate:
        print('looping')
        with lock:
             print "Executing synchronized operation"
        time.sleep(1)
    print('stopping thread')

def run():
    while True:
        time.sleep(2)
        with lock:
             print "Executing another synchronized operation"
        print('main')

这里有一个严重的问题:当run()持有lock时,可能会收到一个信号(例如^C)。如果发生这种情况,您的信号处理程序将在锁仍然保持的情况下运行。然后,它将等待test\u循环退出,如果该线程正在等待锁,您将死锁。在

这是一个完整的问题类别,这就是为什么许多api不从信号处理程序中调用它们。相反,您应该设置一个标志,告诉主线程在适当的时候关闭。在

^{2}$

我倾向于避免退出程序系统出口完全和显式地在主出口点(例如run()的末尾)进行清理,但是如果需要,可以在这里使用atexit。在

我不确定这是否完全改变了,但这就是我在2.6.5中实现atexit的方式


atexit.register(goodbye)

def goodbye():
    print "\nStopping..."

这里的根差异实际上与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处理程序。由于处理程序本身告诉线程,所以它本身就是死锁。在

相关问题 更多 >