已连接的Python进程不会调用atexit

3 投票
1 回答
1640 浏览
提问于 2025-04-28 10:02

我原以为Python进程在结束时会调用它们的atexit函数。请注意,我使用的是Python 2.7。这里有一个简单的例子:

from __future__ import print_function
import atexit
from multiprocessing import Process


def test():
    atexit.register(lambda: print("atexit function ran"))

process = Process(target=test)
process.start()
process.join()

我本来期待这段代码会打印出“atexit函数运行了”,但实际上并没有。

另外,这个问题:Python进程不会调用atexit也有点类似,但它涉及的是被信号终止的进程,而答案是关于如何拦截那个信号的。这个问题中的进程是正常退出的,所以(就我所知)那个问题和答案并不适用(除非这些进程是因为某种信号而退出的?)。

暂无标签

1 个回答

4

我查了一下在CPython中是怎么实现这个的。这里假设你是在Unix系统上运行。如果你是在Windows上,下面的内容可能不适用,因为在multiprocessing中的进程实现有所不同。

结果发现,os._exit()总是在进程结束时被调用。结合下面这段来自atexit文档的说明,可以解释为什么你的lambda函数没有运行。

注意:通过这个模块注册的函数不会在程序被未被Python处理的信号终止时被调用,也不会在检测到Python致命内部错误时被调用,或者在调用os._exit()时被调用。


这是CPython 2.7中Popen类的一个摘录,用于创建子进程。注意,子进程的最后一条语句是调用os._exit()

# Lib/multiprocessing/forking.py

class Popen(object):

    def __init__(self, process_obj):
        sys.stdout.flush()
        sys.stderr.flush()
        self.returncode = None

        self.pid = os.fork()
        if self.pid == 0:
            if 'random' in sys.modules:
                import random
                random.seed()
            code = process_obj._bootstrap()
            sys.stdout.flush()
            sys.stderr.flush()
            os._exit(code)

在Python 3.4中,如果你启动一个子进程,os._exit()依然存在,这是默认的行为。不过似乎你可以更改这个设置,具体可以查看上下文和启动方法获取更多信息。我还没有尝试过,但也许使用spawn的启动方法会有效?不过在Python 2.7中是不可用的。

撰写回答