如何在Python的多进程子进程中注册“atexit”函数?

15

我有一些子进程(使用多进程),当它们停止时,每个都需要做一些最终工作。就像以下示例一样,尽管没有起作用...

import multiprocessing
import atexit

def final():
    print "final work"

def worker():
    print 'Doing some work'
    atexit.register(final)

if __name__ == '__main__':
    p = multiprocessing.Process(target=worker)
    p.start()
    p.join()

那么我应该怎么做呢?


请查看 https://dev59.com/wXM_5IYBdhLWcg3wyWSt,这与您所尝试实现的内容类似。 - Selçuk Cihan
2个回答

14

通过多进程启动的进程不能正常退出,atexit函数永远不会被调用。你需要通过try/finally块手动运行清理程序,例如:

def worker():
    try:
        print('Doing some work')
    finally:
        final()

或者,如果您真的想使用 atexit(请参阅下面的缺点):

def worker():
    try:
        print('Doing some work')
    finally:
        atexit._run_exitfuncs()

为什么子进程不能正常退出?

技术上讲,这是因为它们通过os._exit()退出, 跳过任何清理工作(包括atexit函数,__del__()和弱引用的终结器)。

这个决定背后的原因是,清理工作可能会在错误的时间、错误的进程中执行。大多数使用atexit(或其他机制)的代码都希望代码只被执行一次,并且只在主解释器退出时执行(例如,可以使用atexit来删除临时目录)。每次子进程退出时执行atexit函数,您就会打破这种期望,可能会发生不好的事情。

因此,即使您可以通过atexit._run_exitfuncs()运行atexit函数,也要避免这样做,因为该函数未记录文档,而且您可能不是唯一使用atexit的用户(其他第三方库可能也在使用它,您可能会在其代码中引入错误)。


9
这是一个荒谬的决定。atexit函数不期望从“main”中被调用,它们期望在注册它们的进程中被调用。因此,atexit仅需要存储注册时的pid即可知道它们是否可以被调用。任何使用atexit的模块现在在多进程作业中都无用了……这迫使我使用Popen并pickle自己的状态。实际上,我fork了multiprocessing只是为了将正确的清除操作放回去。 - Erik Aronesty
1
我非常同意你的看法,Erik。我制作了一个更好的atexit替代品,也许你可以看一下?https://github.com/kuralabs/multiexit - Havok

0
您可以使用这个多进程感知的 atexit 替代方案。
它只会调用由该进程本身注册的函数,而不会调用任何由父进程或其他继承的函数。

https://github.com/kuralabs/multiexit


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接