问题: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()
我添加的打印输出PID
,以及一个time.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模块为模块提供了非常易读的解释 ; “要等到进程完成工作并退出后,请使用join()方法。”,但看来至少OS X仍在这样做。
我也想知道该方法的名称。该.join()
方法在此处串联吗?它是在连接过程的结尾吗?还是只是与Python的本地.join()
方法共享一个名称?
回答 0
该join()
方法与threading
或一起使用时multiprocessing
不相关str.join()
-它实际上并没有将任何内容串联在一起。相反,它仅表示“等待此[线程/进程]完成”。join
之所以使用该名称,是因为该multiprocessing
模块的API看起来类似于该threading
模块的API,并且该threading
模块join
用于其Thread
对象。join
在许多编程语言中,使用该术语表示“等待线程完成”是很常见的,因此Python也采用了它。
现在,您看到有和没有调用都延迟20秒的原因join()
是因为默认情况下,当主进程准备退出时,它将隐式调用join()
所有正在运行的multiprocessing.Process
实例。在multiprocessing
文档中并未对此进行明确说明,但在“ 编程指南”部分中进行了提及:
还请记住,非守护进程将自动加入。
您可以通过设置覆盖此行为daemon
上的标志Process
来True
之前,要启动的过程:
p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.
如果这样做,则子进程将在主进程完成后立即终止:
守护程序
进程的守护程序标志,一个布尔值。必须在调用start()之前进行设置。
初始值是从创建过程继承的。
进程退出时,它将尝试终止其所有守护程序子进程。
回答 1
没有 join()
,主进程可以在子进程之前完成。我不确定在什么情况下会导致僵尸。
主要目的 join()
是确保子流程在主流程执行任何依赖于子流程的工作之前完成。
的词源join()
是与的相反fork
,后者是Unix系列操作系统中用于创建子进程的常用术语。单个过程“分叉”成多个,然后“连接”成一个。
回答 2
我不会详细解释join
它的作用,但是这里是它的词源和直觉,这应该有助于您更轻松地记住它的含义。
这个想法是执行“ 分叉 ”到多个进程中,其中一个是主进程,其余是工人(或“奴隶”)。工作人员完成后,他们“加入”主服务器,以便可以恢复串行执行。
该join
方法使主进程等待工作人员加入。该方法最好称为“等待”,因为这是它在主服务器中引起的实际行为(这就是POSIX中所称的内容,尽管POSIX线程也称其为“ join”)。连接仅是由于线程正确协作而产生的,不是主服务器执行的操作。
自1963年以来,名称“ fork”和“ join”已在多处理中使用此含义。
回答 3
join()
用于等待辅助进程退出。必须先调用close()
或terminate()
使用join()
。
就像@罗素提到的 的那样,join就像fork的相反对象(子流程)。
要运行加入,您必须先运行close()
,这将阻止所有其他任务提交到池中,并在所有任务完成后退出。或者,terminate()
通过立即停止所有工作进程退出运行。
"the child process will sit idle and not terminate, becoming a zombie you must manually kill"
当主(父)进程退出但子进程仍在运行并且完成后,它没有父进程可以将其退出状态返回时,这是可能的。
回答 4
的 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()
回答 5
要等待进程完成其工作并退出,请使用join()方法。
和
注意终止进程后,必须对join()进程进行处理,以便让后台机制有时间更新对象的状态以反映终止。
这是一个很好的例子,帮助我理解了:这里
我个人注意到的一件事是,我的主要过程暂停了,直到孩子使用join()方法完成了过程,这首先挫败了我的使用multiprocessing.Process()
意图。