【问题标题】:what content would be copied to sub process from parent processs at making a new sub process in python with multiprocessing modules在带有多处理模块的python中创建一个新的子进程时,哪些内容将从父进程复制到子进程
【发布时间】:2019-01-10 20:48:12
【问题描述】:

有些文章告诉我,创建一个新的子进程,操作系统几乎会从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]

不知道为什么ids函数init()的变化没有复制到子进程,但是全局部分ids.extend([1, 2, 3, 4])的变化对子进程是可见的。

感谢您的每一个回复。

【问题讨论】:

  • 你在哪个平台上?什么 Python 版本?
  • 我怀疑你是在Windows上,没有fork,所以默认的启动方式是"spawn"。它的作用是启动一个全新的 Python 解释器并导入您的模块——这意味着像 ids = []ids.extend(…) 这样的顶级代码将在子进程中运行(但受 __main__ 保护的代码不会运行),所以你最终得到一个相等的列表,但它实际上并不是从父级复制的,它只是以相同的方式创建的。
  • @abarnert 非常感谢您给我如此明确的答复。是的,我在 Windows 7 和 python3.7 上运行它。正如你所说,在 windows 上没有 fork。所以,我在 centos6.5 上得到了预期的结果。

标签: python python-multiprocessing


【解决方案1】:

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

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

  • spawn 创建一个全新的进程,启动 Python 解释器,然后imports 你的模块。

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


由于您使用的是 Windows,因此子代不会从父代获得 ids 的副本,但 ids = []ids.extend(…) 代码会在 import 上运行,因此它们最终都会得到相同的反正价值观。但是任何受__main__ 保护的代码都不会由import 运行,因此它们不会调用main,因此不会调用init,因此不会调用ids.append(-100)


multiprocessing 库的设计目的是让您可以在所有平台上以相同的方式使用它。3 大部分细节都在Programming guidelines 部分的docs,但基本思想是:不要假设全局变量被复制,或者它们没有被复制。

这意味着除了importdefclass 语句以及一些简单的全局常量赋值之外,您通常不会有任何代码在顶级__main__ 保护之外。

您需要做的任何复杂设置,都在每个子进程中进行。4 您想在进程之间共享的任何内容(如LockQueue),您都可以在内部创建__main__ 守卫并作为参数传递给孩子们。


1。对于forkserver,详见文档;它主要适用于那些使用库的程序,这些库希望使用与fork 无法正常工作的线程做一些花哨的事情,这在 macOS 上很常见——尽管它在其他一些情况下很有用。

2。打开文件之类的事情有些复杂,但在这里它们并不重要。

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

4.如果您使用的是Pools,请使用initializer 函数。

【讨论】:

    猜你喜欢
    • 2017-12-29
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多