date: 2021-07-28title: multiprocessing模块之Process类 #标题
tags: #标签
categories: python # 分类

整理下multiprocessing模块中的Process类 使用方法。

参考:老男孩教育。

Python multiprocessing 模块提供了 Process 类,该类可用来创建新进程。使用 Process 类创建多进程有以下 2 种方式:

  • 直接创建 Process 类的实例对象,由此就可以创建一个新的进程;
  • 通过继承 Process 类的子类,创建实例对象,也可以创建新的进程。注意,继承 Process 类的子类需重写父类的 run() 方法。

不仅如此,Process 类中也提供了一些常用的属性和方法,如下:

属性名或方法名 功能
run() 第 2 种创建进程的方式需要用到,继承类中需要对方法进行重写,该方法中包含的是新进程要执行的代码。
start() 和启动子线程一样,新创建的进程也需要手动启动,该方法的功能就是启动新创建的线程。
join([timeout]) 和 thread 类 join() 方法的用法类似,其功能是在多进程执行过程,其他进程必须等到调用 join() 方法的进程执行完毕(或者执行规定的 timeout 时间)后,才能继续执行;
is_alive() 判断当前进程是否还活着。
terminate() 中断该进程。
name属性 可以为该进程重命名,也可以获得该进程的名称。
daemon 和守护线程类似,通过设置该属性为 True,可将新建进程设置为“守护进程”。
pid 返回进程的 ID 号。大多数操作系统都会为每个进程配备唯一的 ID 号。

Process基础语法

  1. import os
  2. from multiprocessing import Process
  3. def func(name):
  4. print(f'当前是{name}进程,pid为{os.getpid()}')
  5. def main():
  6. func('父')
  7. p = Process(target=func, args=('子',)) # 通过Process类创建进程
  8. p.start() # 开启进程
  9. if __name__ == '__main__':
  10. main()

Process中join的用法

假设我们要写一个发送邮件的程序,当所有邮件发送完成后,输出一句提示信息:所有文件已发送成功,那么我们觉得代码应该是这样的:

  1. import time
  2. import random
  3. from multiprocessing import Process
  4. def send_email(name):
  5. time.sleep(random.random())
  6. print(f'给{name}发邮件成功')
  7. def main():
  8. name_list = ('张三', '李四', '王五')
  9. for n in name_list:
  10. p = Process(target=send_email, args=(n,))
  11. p.start()
  12. print('所有邮件发送完成!')
  13. if __name__ == '__main__':
  14. main()
  15. # 但执行后效果却是下面这样的:
  16. 所有邮件发送完成! # 一个也没发送呢,发送完成的提示消息就出来了
  17. 给张三发邮件成功
  18. 给李四发邮件成功
  19. 给王五发邮件成功

通过上面返回的结果可以看出,我们需要的是等所有邮件发送成功后,再打印所有邮件发送完成的提示消息,所以需要用到Process类的join方法,修改后的代码如下:

  1. import time
  2. import random
  3. from multiprocessing import Process
  4. def send_email(name):
  5. time.sleep(random.random())
  6. print(f'给{name}发邮件成功')
  7. def main():
  8. name_list = ('张三', '李四', '王五')
  9. p_lst = [] # 定义一个空列表,用于接收所有子进程地址
  10. for n in name_list:
  11. p = Process(target=send_email, args=(n,))
  12. p.start()
  13. p_lst.append(p)
  14. for p in p_lst: p.join() # 当所有子进程结束后,再继续执行后面的代码
  15. print('所有邮件发送完成!')
  16. if __name__ == '__main__':
  17. main()

使用Process类写一个多进程聊天工具

server端代码

  1. import socket
  2. import time
  3. from multiprocessing import Process
  4. def talk(conn):
  5. while 1:
  6. try:
  7. msg = conn.recv(1024).decode('utf-8')
  8. print(msg)
  9. time.sleep(0.3)
  10. conn.send(msg.upper().encode('utf-8'))
  11. except ConnectionResetError as e:
  12. break
  13. conn.close
  14. def main():
  15. ip_port = ('127.0.0.1', 9011)
  16. sk = socket.socket()
  17. sk.bind(ip_port)
  18. sk.listen()
  19. while 1:
  20. conn, addr = sk.accept()
  21. Process(target=talk,args=(conn,)).start()
  22. sk.close()
  23. if __name__ == '__main__':
  24. main()

client端代码

  1. # 无论开启多少个下面这段代码,都可以正常和server端收发消息
  2. import socket
  3. ip_port=('127.0.0.1',9011)
  4. sk=socket.socket()
  5. sk.connect(ip_port)
  6. while 1:
  7. sk.send(b'hello')
  8. msg=sk.recv(1024).decode('utf-8')
  9. print(msg)
  10. sk.close()

Process开启进程的另一种方式

  1. import os
  2. from multiprocessing import Process
  3. import time
  4. # 定义一个对象,继承Process对象
  5. class MyProcess(Process):
  6. # __init__ 方法是为了接收传参
  7. def __init__(self, a, b, c):
  8. self.a = a
  9. self.b = b
  10. self.c = c
  11. super().__init__() # super函数用于执行父类的init方法,父类Process的init方法还为我们做了很多工作
  12. def run(self): # 将你想执行的代码,放入到run这个函数中(名字必须为run)
  13. time.sleep(1)
  14. print(os.getppid(), os.getpid(), self.a, self.b, self.c)
  15. def main():
  16. print('>>> ', os.getpid())
  17. for i in range(10): # 开启十个子进程
  18. p = MyProcess(3, 4, 7)
  19. p.start()
  20. if __name__ == '__main__':
  21. main()

这种开启进程的方式,和文章开头开启进程的方式相比,区别在于这种方式是面向对象编程,而文章开头是面向函数编程,没有其他区别,自行选择适合自己的编程风格即可。

Process其他属性和方法

import os
from multiprocessing import Process
import time


# 定义一个对象,继承Process对象
class MyProcess(Process):
    # __init__ 方法是为了接收传参
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
        super().__init__()  # super函数用于执行父类的init方法,父类Process的init方法还为我们做了很多工作

    def run(self):  # 将你想执行的代码,放入到run这个函数中(名字必须为run)
        time.sleep(1)
        print(os.getppid(), os.getpid(), self.a, self.b, self.c)


def main():
    print('父进程pid:', os.getpid())
    p = MyProcess(3, 4, 7)
    p.start()
    print('子进程ID:', p.pid)  # 在父进程中打印子进程的pid
    print('子进程名字:', p.name)  # 在父进程中打印子进程的名称
    print('子进程标识符:', p.ident)  # 打印子进程的标识符,其实就是pid
    print(p.is_alive())  # 判断子进程是否存活
    p.terminate()  # 强制结束子进程,结束子进程后,子进程中的代码将不继续执行
    # 短暂休眠后再次判断进程是否存活
    time.sleep(0.01)
    print(p.is_alive())


if __name__ == '__main__':
    main()



# 打印如下:
父进程pid: 12720
子进程ID: 17320
子进程名字: MyProcess-1
子进程标识符: 17320
True
False

守护进程

不是特别重要,请观看B站视频自行理解。