Python 多处理模块的. join()方法到底在做什么?

了解 Python多重处理(从 PMOTW 文章) ,并希望能够对 join()方法的具体操作做一些说明。

2008年的老教程中,它声明如果没有下面代码中的 p.join()调用,“子进程将处于空闲状态而不会终止,成为必须手动杀死的僵尸”。

from multiprocessing import Process


def say_hello(name='world'):
print "Hello, %s" % name


p = Process(target=say_hello)
p.start()
p.join()

我添加了 PIDtime.sleep的打印输出来进行测试,据我所知,这个过程会自动终止:

from multiprocessing import Process
import sys
import time


def say_hello(name='world'):
print "Hello, %s" % name
print 'Starting:', p.name, p.pid
sys.stdout.flush()
print 'Exiting :', p.name, p.pid
sys.stdout.flush()
time.sleep(20)


p = Process(target=say_hello)
p.start()
# no p.join()

在20秒内:

936 ttys000    0:00.05 /Library/Frameworks/Python.framework/Versions/2.7/Reso
938 ttys000    0:00.00 /Library/Frameworks/Python.framework/Versions/2.7/Reso
947 ttys001    0:00.13 -bash

20秒后:

947 ttys001    0:00.13 -bash

行为是相同的 p.join()添加回文件的结尾。Python Module of the Week 提供了一个 这个模块非常易读的解释; “要等到进程完成工作并退出,请使用 join ()方法。”不过看起来至少 OS X 是这么做的。

我还想知道该方法的名称。.join()方法在这里连接了什么吗?它是否连接了一个过程和它的结束?或者它只是与 Python 的本机 .join()方法共享一个名称?

128751 次浏览

如果没有 join(),主进程可以在子进程之前完成。我不知道在什么情况下会导致僵尸。

join()的主要目的是确保子进程在主进程执行依赖于子进程工作的任何操作之前已经完成。

join()的词源是它与 fork相反,fork是 Unix 系列操作系统中创建子进程的常用术语。一个进程“分叉”成几个,然后“联合”成一个。

join()方法,当与 threadingmultiprocessing一起使用时,与 str.join()无关——它实际上并没有将任何东西连接在一起。相反,它只是意味着“等待这个[线程/进程]完成”。之所以使用 join这个名称,是因为 multiprocessing模块的 API 看起来与 threading模块的 API 类似,而 threading模块使用 join作为其 Thread对象。使用术语 join表示“等待线程完成”在许多编程语言中都很常见,因此 Python 也采用了它。

现在,您看到有和没有调用 join()的20秒延迟的原因是,默认情况下,当主进程准备退出时,它将对所有正在运行的 multiprocessing.Process实例隐式调用 join()。这在 multiprocessing文档中没有明确说明,但是在 程序编制指引部分中提到了:

还要记住,非守护进程将自动成为 加入。

在启动进程之前,可以通过将 Process上的 daemon标志设置为 True来覆盖此行为:

p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.

如果这样做,子进程 将在主程序完成后立即终止:

Daemon

进程的守护进程标志,一个布尔值。这必须在 Start ()被调用。

初始值从创建过程继承。

当一个进程退出时,它会尝试终止它的所有守护进程 子进程

我不打算详细解释 join是做什么的,但是这里有它的词源和它背后的直觉,它应该能帮助你更容易地记住它的意思。

其思想是将“ 叉子”执行到多个进程中,其中一个是 总台/初选进程,其余的是 worker (或 未成年人/次要的)。当工人完成后,他们“加入”总台进程,以便可以继续执行串行操作。

join()导致 总台进程等待工作者加入它。这个方法最好叫做“ wait”,因为这是它在 master 中导致的实际行为(在 POSIX 也是这么叫的,尽管 POSIX 线程也叫它“ join”)。联接只是线程正确合作的结果,而不是 总台进程 是的的结果。

在多处理 从1963年开始中,“ fork”和“ join”这两个名称就是这个意思。

join()用于等待辅助进程退出。在使用 join()之前必须调用 close()terminate()

就像@Russell 提到的,加入叉子(产生子进程)是相反的。

要运行 join,必须运行 close(),这将防止任何更多的任务被提交到池中,并在所有任务完成后退出。或者,通过立即停止所有辅助进程,运行 terminate()将退出。

当主进程(父进程)退出,但子进程仍在运行,并且一旦完成,它就没有父进程可以返回其退出状态时,"the child process will sit idle and not terminate, becoming a zombie you must manually kill"可能会出现这种情况。

join()调用确保在完成所有多处理进程之前不会调用代码的后续行。

例如,如果没有 join(),下面的代码甚至会在进程完成之前调用 restart_program(),这类似于异步,并不是我们想要的(您可以尝试) :

num_processes = 5


for i in range(num_processes):
p = multiprocessing.Process(target=calculate_stuff, args=(i,))
p.start()
processes.append(p)
for p in processes:
p.join() # call to ensure subsequent line (e.g. restart_program)
# is not called until all processes finish


restart_program()

若要等待进程完成工作并退出,请使用 join ()方法。

还有

注意: 为了给后台机器时间来更新对象的状态以反映终止,在终止进程之后加入()进程非常重要。

这是一个很好的例子,帮助我了解它: 给你

我个人注意到的一件事情是,我的主进程暂停了,直到子进程使用 join ()方法完成了它的进程,这个方法最初打破了我使用 multiprocessing.Process()的想法。