这是我正在处理的玩具问题:
import multiprocessing as mp
def task2():
print "I am doing something important."
def task1():
listOfProcesses = []
for i in range(5):
process = mp.Process(target=task2)
process.start()
listOfProcesses.append(process)
def task0():
listOfProcesses = []
for i in range(5):
process = mp.Process(target=task1)
process.start()
listOfProcesses.append(process)
if __name__ == '__main__':
task0()
现在,我无法理解在这种情况下应该调用join的位置。
如果我更改task0
的定义,请执行以下操作:
def task0():
listOfProcesses = []
for i in range(5):
process = mp.Process(target=task2)
process.start()
listOfProcesses.append(process)
for process in listOfProcesses:
process.join()
然后一切似乎都正常,但我不明白我在这里做了什么。 task1
只会启动它的子节点,它不会加入它们。那么加入task0
对于task1
意味着什么?
答案 0 :(得分:2)
join
在概念上相当简单 - x.join
说"执行的当前线程(即进程)无法继续此点,直到{{1} }终止。"
因此,一般情况下,您不希望主主题超过某一点,直到所有工作人员完成工作为止。由于您在主线程中执行x
,执行task0
会阻止您的主线程继续经过该点,直到您的所有工作人员(join
和 { {1}})已完成。
task1
中没有task2
!那是对的。但join
的进程仍然无法终止,直到其所有task1
完成为止。这与process groups的POSIX概念有关 - 父进程在其所有子进程终止之前不会终止。所以,让我们看看这个简化示例的输出:
task1
输出:
task2
正如您所看到的,import multiprocessing as mp
from time import sleep
def task2():
sleep(1)
print "I am doing something important."
def task1():
for i in range(2):
process = mp.Process(target=task2)
process.start()
print 'task1 done'
def task0():
process = mp.Process(target=task1)
process.start()
process.join()
if __name__ == '__main__':
task0()
print 'all done'
已达到终点,但在子进程执行之前没有终止 - 这意味着task1 done
I am doing something important.
I am doing something important.
all done
中的task1
块正确阻止了我们的主线程终止直到所有工人都这样做。
为了好玩,以下是运行原始脚本时join
的输出,其中没有task0
,而仅修改被ps jf
投入join
1}}所以我可以抓住它运行:
time.sleep
你可以看到我们的主要流程(task2
)和#34;第一个孩子" (那些 PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
6780 7385 7385 7385 pts/11 7677 Ss 1000 0:00 bash
7385 7677 7677 7385 pts/11 7677 R+ 1000 0:00 \_ ps jf
6780 6866 6866 6866 pts/7 7646 Ss 1000 0:00 bash
6866 7646 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7646 7647 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7647 7672 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7647 7673 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7647 7674 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7647 7675 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7647 7676 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7646 7648 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7648 7665 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7648 7666 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7648 7667 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7648 7668 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7648 7669 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7646 7649 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7649 7656 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7649 7657 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7649 7658 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7649 7659 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7649 7660 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7646 7650 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7650 7652 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7650 7653 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7650 7654 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7650 7655 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7650 7670 7646 6866 pts/7 7646 S+ 1000 0:00 | \_ python test
7646 7651 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7651 7661 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7651 7662 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7651 7663 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7651 7664 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
7651 7671 7646 6866 pts/7 7646 S+ 1000 0:00 \_ python test
)仍然活着,即使它们显然没有python代码来执行。他们也是同一流程组(task0
)的所有成员。
所有这些都是一种冗长的说法:主要线程中的task1
通常是您所需要的,因为您可以保证任何子进程都会等待他们的孩子们在自己终止之前就要终止了。
答案 1 :(得分:1)
在类Unix系统(Linux,BSD等)上,mp.Process
实际调用os.fork
,生成的流程对象的join
方法调用wait
(或变种) 1 等待它(即,等待特定的过程,而不仅仅是任何任意过程)。
fork
的孩子只能wait
- 由其父母 2 编辑,因此task0
可以等待每个task1
,但不适用于任何task1
的{{1}}。同时,每个task2
可以等待所有task1
个task2
,但不能等待任何其他task1
的{{1}}。
因为每个task2
非常短(并且每个进程从其task2
函数返回时都会退出),所以无论您是否明确target=
,都很难看到这些差异。您需要做一些较慢的事情(例如,join
或做一些实际工作)才能看到任何真正的差异。
1 实际通话为time.sleep()
;见os.waitpid()
。实际的通话是multiprocessing/forking.py
功能。
2 如果父进程在没有等待其子进程的情况下退出,那么这些子进程将被“孤立”并作为代理父进程传递给PID 1(init)。进程1循环调用poll
(或等效的)来清理它们。
(Windows变体使用不同的调用 - 例如它不能wait
- 我在Windows上不起作用,所以我不确定那里的事情是如何表现的。)