Python多处理:如果进程'孩子有孙子孙女?那么应该在哪里调用join()?

时间:2014-05-17 23:42:27

标签: python python-2.7 multiprocessing

这是我正在处理的玩具问题:

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意味着什么?

2 个答案:

答案 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可以等待所有task1task2,但不能等待任何其他task1的{​​{1}}。

因为每个task2非常短(并且每个进程从其task2函数返回时都会退出),所以无论您是否明确target=,都很难看到这些差异。您需要做一些较慢的事情(例如,join或做一些实际工作)才能看到任何真正的差异。


1 实际通话为time.sleep();见os.waitpid()。实际的通话是multiprocessing/forking.py功能。

2 如果父进程在没有等待其子进程的情况下退出,那么这些子进程将被“孤立”并作为代理父进程传递给PID 1(init)。进程1循环调用poll(或等效的)来清理它们。


(Windows变体使用不同的调用 - 例如它不能wait - 我在Windows上不起作用,所以我不确定那里的事情是如何表现的。)