了解Python Multiprocessing(来自PMOTW article)并希望澄清join()
方法究竟在做什么。
在old tutorial from 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模块提供very readable explanation of the module; "要等到进程完成其工作并退出,请使用join()方法。",但似乎至少OS X仍在执行此操作。
我也想知道这个方法的名称。 .join()
方法是否在这里连接任何内容?它是否将一个过程与它结束?或者它只是与Python的本地.join()
方法共享一个名称?
答案 0 :(得分:82)
join()
方法与threading
或multiprocessing
一起使用时,与str.join()
无关 - 它实际上并不是连接在一起。相反,它只是意味着“等待[线程/进程]完成”。使用名称join
是因为multiprocessing
模块的API看起来与threading
模块的API类似,threading
模块使用join
Thread
对象。使用术语join
来表示“等待线程完成”在许多编程语言中很常见,因此Python也采用了它。
现在,您在调用join()
的情况下看到20秒延迟的原因是因为默认情况下,当主进程准备退出时,它将隐式调用所有join()
运行multiprocessing.Process
个实例。 multiprocessing
文档中没有明确说明这一点,但Programming Guidelines部分提到了这一点:
还要记住,非守护进程将自动进行 接合。
您可以在开始此过程之前将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.
如果您这样做,则子进程will be terminated as soon as the main process completes:
<强>守护程序强>
进程的守护进程标志,一个布尔值。这必须在之前设置 调用start()。
初始值继承自创建过程。
当进程退出时,它会尝试终止其所有守护进程 子进程。
答案 1 :(得分:23)
如果没有join()
,主进程可以在子进程执行之前完成。我不确定在什么情况下会导致僵尸。
join()
的主要目的是确保在主进程执行任何取决于子进程工作的任何事情之前完成子进程。
join()
的词源是fork
的反义词,这是用于创建子进程的Unix系列操作系统中的常用术语。一个过程&#34;分叉&#34;进入几个,然后&#34;加入&#34;回到一个。
答案 2 :(得分:10)
我不会详细解释join
的作用,但这里的词源及其背后的直觉,可以帮助你更容易地记住它的含义。
这个想法是执行&#34; forks&#34;分为多个过程,其中一个是主人,其余是工人(或者#34;奴隶&#34;)。当工人完成后,他们就会加入&#34;主机,以便可以恢复串行执行。
join
方法导致主进程等待worker加入它。该方法可能更好地被称为&#34; wait&#34;,因为它是它在主服务器中引起的实际行为(以及它在POSIX中调用的内容,尽管POSIX线程称之为&#34;加入&#34;以及)。加入仅在线程正确协作的情况下发生,它不是主所做的事情。
名字&#34; fork&#34;和&#34;加入&#34;已经在多处理since 1963中使用了这个含义。
答案 3 :(得分:3)
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()
答案 4 :(得分:1)
join()
用于等待工作进程退出。在使用close()
之前,必须先致电terminate()
或join()
。
与@Russell一样, join 与 fork (Spawns子流程)相反。
要运行联接,您必须运行close()
,这将阻止任何更多任务提交到池并在所有任务完成后退出。或者,运行terminate()
将立即停止所有工作进程。
"the child process will sit idle and not terminate, becoming a zombie you must manually kill"
当主(父)进程退出但子进程仍在运行时,这是可能的,一旦完成,它没有父进程将其退出状态返回到。
答案 5 :(得分:0)
要等到进程完成工作并退出之前,请使用join()方法。
和
注意:终止进程后,必须加入join()进程,以使后台机制有时间更新对象的状态以反映终止。
这是一个很好的例子,可以帮助我理解它:here
我个人注意到的一件事是,我的主要过程暂停了,直到孩子使用join()方法完成了过程,而该过程首先使用multiprocessing.Process()
破坏了我的观点。