• Python 多进程编程《一》: 创建进程的三种模式


    进程是操作系统分配资源的最小单位,进程之间是相互隔离的,一般一个应用程序就对应一个进程。进程中可以包含多个线程,同一进程内的多线程可以共享进程内的部分资源。由于进程之间有隔离机制,因此在并发编程中,进程之间更加注重通信(或者说资源共享),而多线程编程更加注重线程同步(协同执行)。

    Python中有 spawn、fork、forkserver 三种创建子进程的模式,创建子进程的模式与操作系统密切相关,不同模式下创建的子进程,所具有的共享资源有所差异。
     

    spawn 模式

    The parent process starts a fresh python interpreter process. The child process will only inherit those resources necessary to run the process object’s run() method. In particular, unnecessary file descriptors and handles from the parent process will not be inherited. Starting a process using this method is rather slow compared to using fork or forkserver.
    Available on Unix and Windows. The default on Windows and macOS.

    • starts a fresh python interpreter process,创建一个新的 Python 解释器, 绕开GIL。
    • 速度比 fork 或者 forkserver 模式慢。
    • windows | macOS 下的默认模式, unix 也支持。
    • 并非继承(或者拷贝)父进程的全部资源,而是主动传入进程对象run方法所需的资源,子进程会拷贝一份传递进来的资源
    		import multiprocessing as mp
    		# ---------------------------------------------------------
    		# 抛出异常,子进程中没有name资源;NameError: name 'name' is not defined
    		
    		def f():
    		    print(name)
    		
    		if __name__ == "__main__":
    		    mp.set_start_method("spawn")
    		    name = "123"
    		    p = mp.Process(target=f)  # name 变量并未拷贝到子进程中,而需要通过参数的形式传递给子进程
    		    p.start()
    		    
    		# ------------------------------------------------------------
    		# 将所需资源传递给子进程
    		def f(name):
    		    print(name)
    		    print(f"id of name: {id(name)}")
    		
    		if __name__ == "__main__":
    		    mp.set_start_method("spawn")
    		
    		    name = "123"
    		    p = mp.Process(target=f, args=(name,))
    		    p.start()
    		    print(f"parent process's id of name: {id(name)}")  # id of name 的值不同,说明子进程会将传递进来的资源深拷贝一份。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 传递某些特殊资源到子进程,如:文件对象|文件句柄、线程锁,不支持;如果子进程需要这类资源,应该在子进程内部自主创建。
    		def f(x):
    		    print(x)
    		
    		if __name__ == "__main__":
    		    mp.set_start_method("spawn")
    		
    		    fb = open("test.txt", "wt")
    		    lock = threading.Lock()
    		    p1 = mp.Process(target=f, args=(fb,))
    		    p1.start()  # TypeError: cannot serialize '_io.TextIOWrapper' object
    		
    		    p2 = mp.Process(target=f, args=(lock,))
    		    p2.start()  # TypeError: can't pickle _thread.lock objects
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 在 “if __name__ == "__main__:” 后创建子进程。

    • 在windows环境下,调用Flask的run方法,以多进程模型启动,会抛出“ValueError: Your platform does not support forking.”, 说明flask中的多进程默认使用 fork 模式,而windows系统并不支持此模式。

    fork 模式

    The parent process uses os.fork() to fork the Python interpreter. The child process, when it begins, is effectively identical to the parent process. All resources of the parent are inherited by the child process. Note that safely forking a multithreaded process is problematic.
    Available on Unix only. The default on Unix.

    • 仅支持unix系统,并且是unix系统的默认创建模式。
    • 使用os.fork创建子进程,os.fork仅支持unix系统,可以类比 fork branch,并且会创建一个新的Python解释器。
    		def f():
    		    time.sleep(30)
    		
    		
    		if __name__ == "__main__":
    		    mp.set_start_method("fork")
    		    
    		    p2 = mp.Process(target=f)
    		    p2.start()
    		    time.sleep(60)  # 通过ps -ef|grep python,发现前30秒有两个python解释器,后30秒只有一个
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 复制父进程的全部资源。

    • 同时支持“文件对象”、“线程锁”等对象传参到子进程的run方法中。

    • 可以在任意位置创建子进程。

    • 如果父进程包含多线程,fork模式存在安全性问题。因此 flask 中仅支持多进程单线程或者单进程多线程。

    • os.fork 返回进程id,该返回值有些特殊,在父进程中打印返回值,会输出子进程的进程id,如果在子进程中打印该值,返回的是0。

      	import os
      	import time
      	
      	pid = os.fork()  # only linux
      	print(f"pid: {pid}")
      	print("Bobby")
      	
      	if pid == 0:
      	    print(f"child process: {os.getpid()}, parent process: {os.getppid()}")
      	else:
      	    print(f"parent process:{pid}")
      	
      	time.sleep(2)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

    forkserver 模式

    When the program starts and selects the forkserver start method, a server process is started. From then on, whenever a new process is needed, the parent process connects to the server and requests that it fork a new process. The fork server process is single threaded so it is safe for it to use os.fork(). No unnecessary resources are inherited.
    Available on Unix platforms which support passing file descriptors over Unix pipes.

    • 仅部分unix系统支持,内部使用os.fork创建子进程。
    • 有三个进程,父进程、server进程(单线程)、子进程, 父进程会根据需要,要求server进程fork子进程。
    • 将子进程运行所必需的资源以run方法的参数形式传递,不支持传递文件对象、线程锁等。
    • 创建子进程需要在“if __name__ == "__main__:” 之后。
       

    设置创建进程的模式

    windows仅支持spawn, unix支持fork、spawn、forkserver(部分系统支持)。在项目main模块的“if __name__ == "__main__:” 下调用“multiprocessing.set_start_method”最多一次。
     

    获取进程上下文

    使用multiprocessing.get_context返回一个上下文对象,上下文对象与multiprocessing有着一致的接口。

     

    参考资料

    彻底搞懂 python进程和线程

  • 相关阅读:
    ProtoBuf的使用
    TikTok如何为独立站引流?
    土豆便宜了吗?python可视化显示价格数据
    PLC-Recorder高速采集西门子S7-300(400) PLC数据的方法(开放以太网协议)
    大数据安全
    ant的basedir内置属性
    Stable diffusion模型种类说明
    7判断环的入口结点8输出倒数第k个
    力扣Hot100-994腐烂的橘子
    20221011study01
  • 原文地址:https://blog.csdn.net/weixin_44815943/article/details/125808576