跳转至

multiprocessing

启动方式

启动方式 spawn fork forkserver
差异 父进程启动新 Python 解释器进程。子进程继承运行进程对象的 run() 方法必需的资源。父进程的非必需文件描述符和句柄不会被继承。相比于 fork 或 forkserver,启动速度较慢。 父进程使用 os.fork() 产生 Python 解释器分叉。子进程在开始时实际上与父进程相同,继承父进程的所有资源。分叉多线程进程可能会有问题。在 Python 3.14 上默认的启动方法将不再为 fork,需要显式指定。 当程序启动并选择 forkserver 启动方法时,将产生一个服务器进程。每当需要新进程时,父进程连接到服务器并请求它分叉一个新进程。服务器进程是单线程的,使用 os.fork() 通常是安全的。没有不必要的资源被继承。
可用性 在 POSIX 和 Windows 平台上可用。默认在 Windows 和 macOS 上。 在 POSIX 系统上可用。默认在除 macOS 之外的 POSIX 上。在 Python 3.12 版本中,默认启动方法将不再为 fork。 在支持通过 Unix 管道传递文件描述符的 POSIX 平台上可用,例如 Linux。
适用场景 适用于多平台,但启动速度较慢。 适用于 POSIX 系统,但需要注意分叉多线程进程可能存在问题。 适用于支持通过 Unix 管道传递文件描述符的 POSIX 平台。
注意事项 在多线程环境下,os.fork() 将引发 DeprecationWarning。建议使用其他启动方法。 在 Python 3.12 版本之后,fork 将不再是默认的启动方法,需要显式指定。 在支持的 POSIX 平台上可用,适用于一些特定的场景。

开始示例

一个简单的多进程示例
通过队列共享数据

import multiprocessing as mp
import os
def foo(q):

    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())
    # 向队列中放入数据
    q.put('hello')

# 特别是,使用 fork 上下文创建的锁不能传递给使用 spawn 或 forkserver 启动方法启动的进程。
if __name__ == '__main__':
    # 不放在main中,unix导入时导致创建额外的子进程
    mp.set_start_method('spawn')
    # 创建一个Queue对象;进程间共享资源
    q = mp.Queue() 
    p = mp.Process(target=foo, args=(q,))
    p.start()
    p.join()
    # 在主进程中从队列中取出数据
    print(q.get())

通过管道共享数据

from multiprocessing import Process, Pipe

def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())   # prints "[42, None, 'hello']"
    p.join()