python 性能优化实例练习三 —— 多进程

1. 进程创建与管理

用到的是multiprocessing模块,from multiprocessing import Process,Python Process类常用属性和方法,更多方法参考官方文档的参考部分:

属性名或方法名 功能
start() 和启动子线程一样,新创建的进程也需要手动启动,该方法的功能就是启动新创建的线程。
run() 第 2 种创建进程的方式需要用到,继承类中需要对方法进行重写,该方法中包含的是新进程要执行的代码。
join([timeout]) 和 thread 类 join() 方法的用法类似,其功能是在多进程执行过程,其他进程必须等到调用 join() 方法的进程执行完毕(或者执行规定的 timeout 时间)后,才能继续执行;
is_alive() 判断某进程是否存活,存活返回True,否则False。
terminate() 中断该进程。
name 可以为该进程重命名,也可以获得该进程的名称。
daemon 和守护线程类似,通过设置该属性为 True,可将新建进程设置为“守护进程”。
pid 返回进程的 ID 号。大多数操作系统都会为每个进程配备唯一的 ID 号。
exitcode 进程运行时为None,如果为-N,表示被信号N结束了。
authkey 进程身份验证,默认是由os.urandom()随机生成32字符的字符串。这个键的用途是设计涉及网络连接的底层进程间的通信提供安全性,这类连接只有在具有相同身份验证才能成功。

如果在windows系统上运行多进程,请把Process放在if __name__ == '__main__':下。Python程序入口 name == ‘main’ 有重要功能(多线程)而非编程习惯

1.1 Process 类的实例对象

(以上表格和以下代码参考Python Process创建进程(2种方法)详解,部分进行了修改)

from multiprocessing import Process
import os

# 定义为进程方法传入的参数
my_tuple = ("http://c.biancheng.net/python/",
            "http://c.biancheng.net/shell/",
            "http://c.biancheng.net/java/")


# 定义一个函数,准备作为新进程的 target 参数,打印my_tuple中的信息
def action(name, *add):
    print("I am child, the pid is %d." % os.getpid())
    for arc in add:
        print("%s --当前进程%d" % (arc, os.getpid()))


if __name__ == '__main__':
    print("I am father, the pid is %d." % os.getpid())
    # 创建子进程,执行 action() 函数
    my_process = Process(target=action, args=("my_process进程", *my_tuple))
    # 启动子进程
    my_process.start()
    my_process.join()
    print("I am father, over")

程序执行结果为:

I am father, the pid is 18968.
I am child, the pid is 18984.
http://c.biancheng.net/python/ --当前进程18984
http://c.biancheng.net/shell/ --当前进程18984
http://c.biancheng.net/java/ --当前进程18984
I am father, over

通过 multiprocessing.Process 来创建并启动进程时,程序必须先判断 if __name__=='__main__':,否则运行该程序会引发异常。

这种运行方式与python中运行线程非常类似,在父进程中Process类的对象,然后产生两个分支,一个分支运行函数action中的程序,一个分支继续运行,最后会和。

1.2 通过Process继承类创建进程

通过继承 Process 类的子类,创建实例对象,也可以创建新的进程。注意,继承 Process 类的子类需重写父类的 run() 方法。

from multiprocessing import Process
import os


# 定义为进程方法传入的参数
my_tuple = ("http://c.biancheng.net/python/",
            "http://c.biancheng.net/shell/",
            "http://c.biancheng.net/java/")


# 自定义一个进程类,从Process继承
class My_Process(Process):
    def __init__(self, name, *add):
        super().__init__()
        self.name = name
        self.add = add

    def run(self):
        print(self.name)
        for arc in self.add:
            print("%s --当前进程%d" % (arc,os.getpid()))


if __name__ == '__main__':
    # 定义为进程方法传入的参数
    print("I am father, the pid is %d." % os.getpid())
    my_process = My_Process("my_process进程", *my_tuple)

    # 启动子进程
    my_process.start()   # 运行的就是My_Process中run方法中的内容

    my_process.join()
    print("I am father, over")

程序执行结果为:

I am father, the pid is 1936.
my_process进程
http://c.biancheng.net/python/ --当前进程23288
http://c.biancheng.net/shell/ --当前进程23288
http://c.biancheng.net/java/ --当前进程23288
I am father, over

除了进程id号不同外,其余都相同。

1.3 多个进程的创建和销毁

以下代码摘录自一篇文章搞定Python多进程(全)

from multiprocessing import  Process


def fun1(idx):
    print('测试%d多进程' % idx)


if __name__ == '__main__':
    process_list = [Process(target=fun1, args=(x,)) for x in range(0, 5)]
    for p in process_list:
        p.start()

    for p in process_list:
        p.join()

    print('结束测试')

程序执行结果为:

测试0多进程
测试1多进程
测试2多进程
测试3多进程
测试4多进程
结束测试

Process finished with exit code 0

上面的代码开启了5个子进程去执行函数,将每个进程加入到list中,然后遍历list进行回收。

在C语言多进程编程中,对于进程的回收有严格的要求,一定要把进程回收,不然就变成了僵尸进程占满系统内存,因此有功能齐全的进程回收函数wait()和waitpid()。在python中目前遇到的回收还没有遇到非常好的内容,先挖一个坑。

1.4 进程的退出状态

from multiprocessing import Process
import time


def first():
    print("There is no problem here")


def second():
    raise RuntimeError("Error raised!")


def third():
    time.sleep(3)
    print("This process will be terminated")


if __name__ == '__main__':
    workers = [Process(target=first), Process(target=second), Process(target=third)]
    for w in workers:
        w.start()

    workers[-1].terminate()

    for w in workers:
        w.join()

    for w in workers:
        print(w.exitcode)

程序执行结果为:

There is no problem here
Process Process-2:
Traceback (most recent call last):
  File "D:\Anaconda\envs\yolo_pytorch38\lib\multiprocessing\process.py", line 315, in _bootstrap
    self.run()
  File "D:\Anaconda\envs\yolo_pytorch38\lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "E:\Code\Yolo_related_code\yolov5-master-main\scripts\processes.py", line 10, in second
    raise RuntimeError("Error raised!")
RuntimeError: Error raised!
0
1
-15

当进程结束(或中断)的时候,会产生一个退出码(exitcode),它是一个数字,表示执行的结果。不同的数字分别表示进程正常完结,异常完结,或是由另一个进程中断的状态。
具体有以下三种情况:
等于0表示正常完结
大于0表示异常完结
小于0表示进程被另一个进程通过-1*exit_code信号终结

1.5 进程创建的方式spawn和fork

有时候创建进程并不嫩加快速度,如果原程序需要导入的库很多很大,由于进程创建的特性,速度反而会变慢。

Python多进程可以选择两种创建进程的方式,spawn 与 fork。分支创建:fork会直接复制一份自己给子进程运行,并把自己所有资源的handle 都让子进程继承,因而创建速度很快,但更占用内存资源。分产创建:spawn只会把必要的资源的handle 交给子进程,因此创建速度稍慢。详细解释请看 Stack OverFlow multiprocessing fork vs spawn

记一次性能优化的心酸历程【Flask+Gunicorn+pytorch+多进程+线程池,一顿操作猛如虎】

2. 进程间通信

2.1 Queue(队列)

Queue类是一个既线程安全又进程安全的先进先出(FIFO,first in first
out,数据交换机制。

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # prints "[42, None, 'hello']"
    p.join()

2.2 Pipe(管道)

2.3 内存共享

3. 进程池

4. 参考链接

一篇文章搞定Python多进程(全)
Python Process创建进程(2种方法)详解
强推这个,这个博主了解的很透彻在Python中优雅地用多进程
官方文档:multiprocessing — 基于进程的并行

猜你喜欢

转载自blog.csdn.net/weixin_42442319/article/details/127771972
今日推荐