我看过很多与此有关的问题...但是我的代码 作品 在Python 2.6.2和 失败 在Python 2.6.5上工作。我认为在程序被信号杀死时,通过此模块注册的整个功能不应该在这里计算,因为我抓住信号然后干净地退出,我不应该调用“通过此模块注册的功能”是错误的吗?这里发生了什么?什么是正确的方法?

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:

$ python halp.py 
looping
looping
looping
main
looping
main
looping
looping
looping
main
looping
^Ccaught signal
shutdown detected
stopping thread

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无关,而是改变的行为 sys.exit.

在2.6.5左右之前 sys.exit (更准确地说,最高级别的SystemExit将导致口译员退出;如果线程仍在运行,则与POSIX线程一样,它们将被终止。

大约2.6.5,行为改变了: sys.exit 现在与从程序的主要功能返回基本相同。当你这样做的时候 - 在两个版本中 - 解释器在退出之前等待所有线程。

相关的更改是 Py_Finalize 现在打电话 wait_for_thread_shutdown() 在顶部附近,没有以前。

这种行为变化似乎不正确,主要是因为它不再如记录的那样发挥作用,这简直就是:“退出python”。实际效果不再是从Python退出,而是仅仅是为了退出线程。 (作为旁注, sys.exit 从另一个线程打电话时,从未退出python,但是与所记载的行为的那种晦涩的分歧并没有证明更大的差异。)

我可以看到新行为的吸引力:而不是退出主线程的两种方法(“退出并等待线程”和“立即退出”),只有一种,因为sys.exit基本上与简单地从返回顶部功能。但是,这是一个破裂的变化,与所记载的行为分歧,这远远超过了这一点。

由于这种变化,之后 sys.exit 从上面的信号处理程序中,解释器坐在等待线程退出然后运行 atexit 处理人员之后。由于处理程序本身告诉线程退出,因此结果是僵局。

其他提示

退出 到期的 信号与退出 之内 信号处理程序。捕获信号并使用sys.exit退出是一个干净的出口,而不是由于信号处理程序而导致的出口。因此,是的,我同意它应该在这里运行至少在这里 - 原则上。

但是,信号处理程序有些棘手:它们完全不同步。他们可以随时在任何VM OPODE之间中断程序流。以此代码为例。 (将其视为与您上面的代码相同的形式;我为简洁省略了代码。)

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()持有时,可能会收到信号(例如 ^c) lock. 。如果发生这种情况,您的信号处理程序将运行,锁定仍保持锁定。然后,它将等待test_loop退出,如果该线程正在等待锁,您将陷入僵局。

这是整个问题的类别,这就是为什么 很多 API说不要从信号处理程序内打电话给它们。相反,您应该设置一个标志来告诉主线程在适当的时间关闭。

do_shutdown = False
def close_handler(signum, frame):
    global do_shutdown
    do_shutdown = True
    print('caught signal')

def run():
    while not do_shutdown:
        ...

我的偏爱是避免完全使用sys.t.exit退出程序,并在主要出口点(例如Run()结束())明确进行清理,但是如果需要,您可以在此处使用Atexit。

我不确定这是否完全改变了,但这就是我在2.6.5中完成我的遇到的方式


atexit.register(goodbye)

def goodbye():
    print "\nStopping..."
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top