Python设置进程启动的3种方式

前面章节中,已经详解介绍了 2 种创建进程的方法,即分别使用 os.fork() 和 Process 类来创建进程。其中:
  • 使用 os.fork() 函数创建的子进程,会从创建位置处开始,执行后续所有的程序,主进程如何执行,则子进程就如何执行;
  • 而使用 Process 类创建的进程,则仅会执行if "__name__"="__main__"之外的可执行代码以及该类构造方法中 target 参数指定的函数(使用 Process 子类创建的进程,只能执行重写的 run() 方法)。

实际上,Python 创建的子进程执行的内容,和启动该进程的方式有关。而根据不同的平台,启动进程的方式大致可分为以下 3 种:
  1. spawn:使用此方式启动的进程,只会执行和 target 参数或者 run() 方法相关的代码。Windows 平台只能使用此方法,事实上该平台默认使用的也是该启动方式。相比其他两种方式,此方式启动进程的效率最低。
  2. fork:使用此方式启动的进程,基本等同于主进程(即主进程拥有的资源,该子进程全都有)。因此,该子进程会从创建位置起,和主进程一样执行程序中的代码。注意,此启动方式仅适用于 UNIX 平台,os.fork() 创建的进程就是采用此方式启动的。
  3. forserver:使用此方式,程序将会启动一个服务器进程。即当程序每次请求启动新进程时,父进程都会连接到该服务器进程,请求由服务器进程来创建新进程。通过这种方式启动的进程不需要从父进程继承资源。注意,此启动方式只在 UNIX 平台上有效。

总的来说,使用类 UNIX 平台,启动进程的方式有以上 3 种,而使用 Windows 平台,只能选用 spawn 方式(默认即可)。

在了解以上 3 种进程启动方式的基础上,我们还需要知道手动设置进程启动方式的方法,大致有以下  2 种。

1) Python multiprocessing 模块提供了一个set_start_method() 函数,该函数可用于设置启动进程的方式。需要注意的是,该函数的调用位置,必须位于所有与多进程有关的代码之前。

例如,下面代码演示了如何显式设置进程的启动方式:
import multiprocessing
import os
print("当前进程ID:",os.getpid())

# 定义一个函数,准备作为新进程的 target 参数
def action(name,*add):
    print(name)
    for arc in add:
        print("%s --当前进程%d" % (arc,os.getpid()))
if __name__=='__main__':
    #定义为进程方法传入的参数
    my_tuple = ("https://www.xinbaoku.com/python/",\
                "https://www.xinbaoku.com/shell/",\
                "https://www.xinbaoku.com/java/")
    #设置进程启动方式
    multiprocessing.set_start_method('spawn')
   
    #创建子进程,执行 action() 函数
    my_process = multiprocessing.Process(target = action, args = ("my_process进程",*my_tuple))
    #启动子进程
    my_process.start()
程序执行结果为:

当前进程ID: 24500
当前进程ID: 17300
my_process进程
https://www.xinbaoku.com/python/ --当前进程17300
https://www.xinbaoku.com/shell/ --当前进程17300
https://www.xinbaoku.com/java/ --当前进程17300

注意,由于此程序中进程的启动方式为 spawn,因此该程序可以在任意( Windows 和类 UNIX 上都可以 )平台上执行。

2) 除此之外,还可以使用 multiprocessing 模块提供的 get_context() 函数来设置进程启动的方法,调用该函数时可传入 "spawn"、"fork"、"forkserver" 作为参数,用来指定进程启动的方式。


需要注意的一点是,前面在创建进程是,使用的 multiprocessing.Process() 这种形式,而在使用 get_context() 函数设置启动进程方式时,需用该函数的返回值,代替 multiprocessing 模块调用 Process()。 


例如,下面程序演示了如何使用 get_context() 函数设置进程启动:
import multiprocessing
import os
print("当前进程ID:",os.getpid())

# 定义一个函数,准备作为新进程的 target 参数
def action(name,*add):
    print(name)
    for arc in add:
        print("%s --当前进程%d" % (arc,os.getpid()))
if __name__=='__main__':
    #定义为进程方法传入的参数
    my_tuple = ("https://www.xinbaoku.com/python/",\
                "https://www.xinbaoku.com/shell/",\
                "https://www.xinbaoku.com/java/")
    #设置使用 fork 方式启动进程
    ctx = multiprocessing.get_context('spawn')
   
    #用 ctx 代替 multiprocessing 模块创建子进程,执行 action() 函数
    my_process = ctx.Process(target = action, args = ("my_process进程",*my_tuple))
    #启动子进程
    my_process.start()
程序执行结果为:

当前进程ID: 18632
当前进程ID: 16700
my_process进程
https://www.xinbaoku.com/python/ --当前进程16700
https://www.xinbaoku.com/shell/ --当前进程16700
https://www.xinbaoku.com/java/ --当前进程16700

以上仅演示了在 Windows 平台上设置进程启动方式的效果,有兴趣的读者可自行尝试选择类 UNIX 平台测试其他启动进程的方式。