在带有多处理模块的python中制作新的子流程时,将从父流程复制到子流程的内容是什么

时间:2018-08-03 02:24:45

标签: python python-multiprocessing

有些文章告诉我,在创建一个新的子进程时,操作系统几乎会复制来自partent的所有数据,包括进程的struct,stack,heap等。因此,我认为可以将全局变量,静态变量复制到子进程中,在调用fork()时,其内容等于父级的值。但是遵循python代码的结果使我感到困惑:

from multiprocessing import Process
ids = []
ids.extend([1, 2, 3, 4])

def worker(sub_id):
    global ids
    print("sub_id=%s, the content of ids: [%s]" % (sub_id, ",".join(["%s" % x for x in ids])))

def init():
    global ids
    ids.append(-100)

def main():
    init()
    sub_process = list()
    for i in range(2):
        process = Process(target=worker, args=(i, ))
        process.start()
        sub_process.append(process)
    for p in sub_process:
        p.join()
    global ids
    ids.append(100)
    print("the main process, the content of ids: [%s]" % (",".join(["%s" % x for x in ids])))

if __name__ == "__main__":
    main()

以上代码的执行结果:

sub_id=0, the content of ids: [1,2,3,4]
sub_id=1, the content of ids: [1,2,3,4]
the main process, the content of ids: [1,2,3,4,-100,100]

我期望的结果:

sub_id=0, the content of ids: [1,2,3,4, -100]
sub_id=1, the content of ids: [1,2,3,4, -100]
the main process, the content of ids: [1,2,3,4,-100,100]

我不知道为什么函数idsinit()的更改不会复制到子进程,而全局节ids.extend([1, 2, 3, 4])中的更改对子进程可见。

感谢您的每次答复。

1 个答案:

答案 0 :(得分:1)

如文档中所述,multiprocessing具有三种不同的启动过程的方式。主要的两个是forkspawn 1

  • fork复制您的父进程。您对此事一无所知: 2 孩子从父母全局变量的副本开始,依此类推。

  • spawn创建一个全新的过程,启动Python解释器,并import来创建您的模块。

在Unix上,默认为fork,但可以选择spawnforkserver。在Windows上,spawn是默认选项,也是唯一的选项,因为Windows不提供fork API。


由于您使用的是Windows,因此子级不会从父级那里获得ids的副本,但是ids = []ids.extend(…)代码会在import上运行,因此无论如何它们最终都具有相等的值。但是受__main__保护的任何代码都不会被import运行,因此它们不会调用main,因此不会init,也不会{{1} }。


ids.append(-100)库的设计使您可以在所有平台上使用相同的方式来使用它。 3 {{3} }部分,但基本思想是:不要假设全局变量已被复制,或者没有被复制。

这意味着,除了multiprocessing__main__import语句之外,顶层def防护层之外通常不会有任何代码,也许一些简单的全局常量分配。

您需要做的任何复杂的设置,都在每个子进程中完成。 4 您想在进程之间共享的任何内容(例如classLock) ,您可以在Queue防护中创建并传递给子级作为参数。


1。对于__main__,请参阅文档以了解详细信息;它主要用于那些使用库的程序,这些程序希望对无法与forkserver配合使用的线程做一些花哨的事情,这在macOS上很常见,尽管它在其他情况下也很有用。

2。诸如打开文件之类的东西有些复杂,但在这里并不重要。

3。您可以可以指定fork,然后将每个平台都视为Windows。但是在某些Unix平台上,spawn进程的速度可能很慢。另外,在Unix上让共享文件之类的东西工作可能会很痛苦,因此有时编写与spawnfork兼容的代码比编写与spawn兼容的代码更容易。 Unix或Windows上的spawn。但是,在Unix上使用spawn和在Windows上使用forkserver可能是一个很好的折衷方案/

4。如果您使用的是spawn,请使用Pool函数。