父进程启动一个新的python解释器进程。子进程将只继承那些运行进程对象run()方法所需的资源。特别是,来自父进程的不必要的文件描述符和句柄将不会被继承。与使用fork或forkerver相比,使用这种方法启动一个进程是相当慢的。 [在Unix和Windows上可用,在Windows和macOS上是默认的]。

父进程使用 os.fork() 分叉 Python 解释器。当子进程开始时,它实际上与父进程是相同的。父进程的所有资源都由子进程继承。注意,安全地分叉一个多线程的进程是有问题的。 [仅在Unix上可用,在Unix上是默认的]。

而我的问题是。

  • is it that the fork is much quicker 'cuz it does not try to identify which resources to copy?
  • is it that, since fork duplicates everything, it would "waste" much more resources comparing to 卵子()?
  • 1 个评论
    由于写时复制,fork的速度很快。 spawn需要建立一个全新的进程。
    python
    multiprocessing
    fork
    python-multiprocessing
    spawn
    Crystina
    Crystina
    发布于 2020-09-28
    2 个回答
    Jerry101
    Jerry101
    发布于 2020-09-28
    已采纳
    0 人赞同

    在3之间有一个权衡 多进程的启动方法 :

  • fork 更快,因为它对父进程的整个虚拟内存进行了写时复制,包括初始化的Python解释器、加载的模块和内存中构造的对象。

    但是fork并没有复制父进程的线程。因此,在父进程中由其他线程持有的锁(在内存中)被卡在子进程中,没有自己的线程来解锁它们,当代码试图获取其中任何一个锁时,就会造成死锁。另外,任何有分叉线程的本地库都会处于崩溃状态。

    复制的 Python 模块和对象可能是有用的,也可能不必要地使每个分叉的子进程变得臃肿。

    子进程也 "继承 "了操作系统资源,如开放的文件描述符和开放的网络端口。这些也会导致问题,但Python可以解决其中的一些问题。

    So fork是快速、不安全的,而且可能是臃肿的。

    然而这些安全问题 不至于引起麻烦 取决于子进程做什么。

  • 卵子 starts a Python child process from scratch without the parent process's memory, file descriptors, threads, etc. Technically, 卵子 forks a duplicate of the current process, then the child immediately calls exec to replace itself with a fresh Python, then asks Python to load the target module and run the target callable.

    So 卵子 is safe, compact, and slower 因为Python必须加载、初始化自己、读取文件、加载和初始化模块,等等。

    However it 可能不会明显变慢 与子进程所做的工作相比。

  • forkserver 分叉出一个当前 Python 进程的副本,该副本可以缩减到大约一个新的 Python 进程。这就成为 "分叉服务器 "进程。然后每次启动一个子进程时,它都会要求分叉服务器分叉一个子进程并运行其目标可调用程序。

    这些子进程开始时都很紧凑,没有卡锁。

    forkserver更加复杂,而且没有很好的文档。 博扬-尼科利奇的博客文章 解释了更多关于forkserver和其秘密的 set_forkserver_preload() 方法来预加载一些模块。要警惕使用一个没有记录的方法,特别是在 bug fix in Python 3.7.0 .

    So forkserver是快速、紧凑和安全的,但它更复杂,而且没有很好的文档。 .

    [文件对这些都不是很好,所以我综合了多个来源的信息并做了一些推断。请对任何错误发表评论。]

  • @michalmonday 如果父进程在分叉子进程时是单线程的,那么 "分叉 "选项会更安全。所以,是的,在启动额外的线程之前,尽早分叉额外的(子)进程。我不知道 "fork "有什么其他安全问题。
    即使模块没有被使用,fork()也不会导致臃肿。这些模块所占用的内存是与父进程共享的,因为fork()是写时复制的,所以如果子进程不使用这些模块,它们不会花费更多的内存,而你还没有使用。
    @LieRyan 的确,如果这些页面不被使用,它们不会花费RAM空间,但它们会增加子进程的地址空间,这可能会使它更接近于内存不足的杀手。另外,在这些页面中添加/删除对任何 Python 对象的引用将更新其引用计数,因此需要复制其页面。Python的周期检测GC可能需要扫描这些页面,从而将它们交换到RAM中,也会耗费GC的工作。
    @Jerry101 如果引用计数需要更新,那么是的,页面可能需要被复制,但这只是意味着模块被实际使用。另一方面,无论模块是否被使用,多进程产卵方法总是对模块进行复制。尽管有refcount和GC,fork需要复制的内容仍然比使用spawn时少很多。
    多进程 spawn 与子进程 spawn 不同。用子进程 spawn,你是在生成一个不同的 Python 程序,它可以有一个不同的 (而且希望是更小的) 加载模块列表。但是用多进程 spawn,初始化会预先加载主进程中加载的所有模块,所以它总是比 fork 更加臃肿。
    Darkonaut
    Darkonaut
    发布于 2020-09-28
    0 人赞同
  • is it that the fork is much quicker 'cuz it does not try to identify which resources to copy?
  • 是的,这要快得多。内核可以克隆整个过程,只复制 modified 内存页 作为一个整体 .将资源输送到一个新的进程并从头开始启动解释器是没有必要的。