多进程操作

(一)sultiprocessing模块介绍

  • python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。
    • Python提供了multiprocessing。
    • multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
  • multiprocessing模块的功能众多:
    • 支持子进程、通信和共享数据、执行不同形式的同步
    • 提供了Process、Queue、Pipe、Lock等组件。
  • 需要再次强调的一点是:
    • 与线程不同,进程没有任何共享状
    • 进程修改的数据,改动仅限于该进程内。

(二)Process类的介绍

(1)创建进程的类

  • 语法
Process([group [, target [, name [, args [, kwargs]]]]])
  • 由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:

  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

(2)参数介绍

  • group参数未使用,值始终为None
  • target表示调用对象,即子进程要执行的任务
  • args表示调用对象的位置参数元组,args=(1,2,'ly',)
  • kwargs表示调用对象的字典,kwargs=
  • name为子进程的名称

(3)方法介绍

  • p.start()
    • 启动进程,并调用该子进程中的p.run()
  • p.run():
    • 进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  • p.terminate():
    • 强制终止进程p,不会进行任何清理操作
    • 如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
    • 如果p还保存了一个锁那么也将不会被释放,进而导致死锁
  • p.is_alive():
    • 如果p仍然运行,返回True
  • p.join([timeout]):
    • 主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。
    • timeout是可选的超时时间
    • 需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

(4)属性介绍

  • p.daemon
    • 默认值为False
    • 如果设为True,代表p为后台运行的守护进程
    • 当p的父进程终止时,p也随之终止
    • 并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
  • p.name:
    • 进程的名称
  • p.pid
    • 进程的pid
  • p.exitcode:
    • 进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
  • p.authkey:
    • 进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
    • 这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

(三)Process类的使用

(1)特别提醒

  • 注意::在windows中Process()必须放到 if name=='main':下

(2)创建并开启子进程的两种方式

(1)方法一:直接使用Process方法

"""直接使用Process的方法"""
# 导入模块
from multiprocessing import Process
def run(name):
    print(f"名称:{name}")
# 使用Process方式实例化得到任务对象
if __name__ == '__main__':
    # 创建进程对象
    # Process(target=被调用的函数(任务名/进程名),args=(参数,))
    # args 是以元组的形式  里面的参数一定要用逗号隔开,哪怕只有一个参数也要用逗号
    p=Process(target=run,args=('syh',))
    p1=Process(target=run,args=('su',))
    p2=Process(target=run,args=('s',))
    # p.start()开启进程
    # 告诉操作系统帮我们创建一个进程
    p.start()
    p1.start()
    p2.start()
# 一定是主进程先跑起来的,再去启动子进程
# 子进程的顺序是不确定的,由操作系统调度
# 名称:s
# 名称:syh
# 名称:su

(2)方法二:继承Process类

from multiprocessing import Process
"""继承Process类"""
# 创建一个新类继承Process
class myProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name=name
    # 定义一个任务函数
    def run(self):
        print(f"{self.name}正在运行!!")


if __name__ == '__main__':
    # 创建子进程
    p=myProcess(name='hhhh')
    p1=myProcess(name='ssss')
    p2=myProcess(name='yyyy')
    # 启动主进程和子进程
    # 告诉操作系统帮我们创建一个进程
    p.start()
    p1.start()
    p2.start()
# hhhh正在运行!!
# ssss正在运行!!
# yyyy正在运行!!
  • 创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
  • 一个进程对应在内存中就是一块独立的空间
  • 多个进程对应在内存中就是多块独立的内存空间
  • 进程与进程之间数据默认情况下是无法直接交互的,如果想交互可以借助第三方工具或模块

(3)进程之间的内存空间是隔离的

  • 每一个子进程之间的数据是相互隔离的
  • 在执行子进程代码时,只修改自己子进程内的数据,不会影响到其他的子进程
# 正常函数修改全局变量
number=99
def text(i):
    global number
    number +=1
    print(f"这是子进程{i}中的数据{number}")

def main():
    text(i=1)
    text(i=2)
    text(i=3)
# 这是子进程1中的数据100
# 这是子进程2中的数据101
# 这是子进程3中的数据102



# 进程之间的修改全局变量是相互隔离的
import multiprocessing

number=99
def text(i):
    global number # 局部修改全局变量
    number +=1
    print(f"这是子进程{i}中的数据{number}")
if __name__ == '__main__':
    for i in range(1,6):
        p=multiprocessing.Process(target=text,args=(i,))
        p.start()
# 这是子进程2中的数据100
# 这是子进程1中的数据100
# 这是子进程5中的数据100
# 这是子进程3中的数据100
# 这是子进程4中的数据100

(4)socket通信变成并发的形式

  • 多个客户端
  • client

# 导入模块
import socket
# 获取客户端对象
client=socket.socket()
# 建立客户端连接
client.connect(('127.0.0.1',9663))
while True:
    send_msg=input("请输入小写字母:").strip()
    if not send_msg:
        continue
    client.send(send_msg.encode('utf-8'))
    # 接收反馈信息
    msg=client.recv(1024)
    print(msg.decode('utf-8'))
  • server
# 导入模块
import socket
server=socket.socket()
from socket import *
import multiprocessing
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
# 获取服务端对象
# 建立服务端连接
server.bind(('127.0.0.1',9663))
# 指定半连接池大小
server.listen()
# 接收信息 和 反馈信息
def recv(conn,addr):
    while True:
        # 检查是否由异常,并处理异常
        try:
            msg=conn.recv(1024)
            if not msg:
                break
            msg=msg.decode('utf-8')
            print(msg)
            print(addr)
            # 反馈信息
            send_msg=msg.upper()
            conn.send(send_msg.encode('utf-8'))
        except Exception as e:
            break
def main():
    while True:
        # 接收客户端连接
        conn,addr=server.accept()
        # 创建进程对象
        p=multiprocessing.Process(target=recv,args=(conn,addr))
        # 启动进程
        p.start()
if __name__ == '__main__':
    main()

(5)Process对象的join方法

  • 将并行改为串行
  • join:主进程等待,等待子进程结束

(1)非join方法---并行

# 没有加入join方法的时候,处于并行状态,
# 将主进程启动起来后,并没有按照顺序拿到子进程的结果
# 导入模块
from multiprocessing import  Process
import os
import time
def test(i):
    print(f"这是进程{i}    子进程号{os.getpid()}   开始!!!")
    # 模拟IO阻塞
    time.sleep(2)
    print(f"这是进程{i}    子进程号{os.getpid()}   结束!!!")
if __name__ == '__main__':
    for i in range(1,4):
        # 创建进程对象
        p=Process(target=test,args=(i,))
        # 启动进程对象
        p.start()
"""
    没有加入join方法的时候,处于并行状态,
    将主进程启动起来后,并没有按照顺序拿到子进程的结果
"""
# 这是进程1    子进程号15148   开始!!!
# 这是进程2    子进程号14420   开始!!!
# 这是进程3    子进程号21896   开始!!!
# 这是进程2    子进程号14420   结束!!!
# 这是进程1    子进程号15148   结束!!!
# 这是进程3    子进程号21896   结束!!!

(2)jion方法---串行

# 加入join 方法的时候,处于串行的状态
#   将主进程启动后,按照串行的执行顺序依次执行子进程,拿到了子进程的结#果后再继续往后执行
#    主进程等待子进程结束后,再结束主进程


    # 我先启动主进程
    # 接着启动每一个子进程
    # 主进程等待所有子进程结束后再结束子进程
# 导入模块
from multiprocessing import  Process
import os
import time
def test(i):
    print(f"这是进程{i}    子进程号{os.getpid()}   开始!!!")
    # 模拟IO阻塞
    time.sleep(2)
    print(f"这是进程{i}    子进程号{os.getpid()}   结束!!!")
if __name__ == '__main__':
    print("开始主进程")
    for i in range(1,4):
        # 创建进程对象
        p=Process(target=test,args=(i,))
        # 启动进程对象
        p.start()
        p.join()
    print("结束主进程")
"""
    加入join 方法的时候,处于串行的状态
    将主进程启动后,按照串行的执行顺序依次执行子进程,拿到了子进程的结果后再继续往后执行
    主进程等待子进程结束后,再结束主进程
"""
# 开始主进程
# 这是进程1    子进程号2228   开始!!!
# 这是进程1    子进程号2228   结束!!!
# 这是进程2    子进程号4704   开始!!!
# 这是进程2    子进程号4704   结束!!!
# 这是进程3    子进程号13652   开始!!!
# 这是进程3    子进程号13652   结束!!!
# 结束主进程

(3)join方法---并行

# join方法 实现并行的步骤

# 创建一个空的进程对象列表
# 创建进程对象
# 启动进程对象
# 将进程对象添加进进程列表中
# 遍历每个子进程
# 再用join()启动每个子进程



# 导入模块
from multiprocessing import  Process
import os
import time
def test(i):
    print(f"这是进程{i}    子进程号{os.getpid()}   开始!!!")
    # 模拟IO阻塞
    time.sleep(2)
    print(f"这是进程{i}    子进程号{os.getpid()}   结束!!!")

if __name__ == '__main__':
    print('进程开始')
    start_time=time.time()
    # 创建一个进程的空列表
    p_list=[]
    for i in range(1,4):
        # 创建进程对象
        p=Process(target=test,args=(i,))
        # 启动进程对象
        p.start()
        # 将启动后的子进程扔进进程列表中
        p_list.append(p)
    # 逐个遍历每一个子进程
    for p in p_list:
        # 启动子进程
        p.join()
    print('进程结束')
    print(f'总的耗时:{time.time()-start_time}')
# 进程开始
# 这是进程1    子进程号2600   开始!!!
# 这是进程2    子进程号2228   开始!!!
# 这是进程3    子进程号1524   开始!!!
# 这是进程1    子进程号2600   结束!!!
# 这是进程2    子进程号2228   结束!!!
# 这是进程3    子进程号1524   结束!!!
# 进程结束
# 总的耗时:2.1254165172576904

多进程开设模板

"""多进程开设模板"""
import os
import time
# 1.导入模块
from multiprocessing import Process

# 2.定义一个存在IO阻塞的进程
def test(i):
    print(f"子进程{i}---开始---进程号为{os.getpid()}")
    time.sleep(3)
    print(f"子进程{i}---结束---进程号为{os.getpid()}")
# 3.开始多进程
def main():
    # 定义一个空的进程列表
    p_list=[]
    for i in range(1,6):
        # 创建进程对象
        p=Process(target=test,args=(i,))
        # 启动进程对象
        p.start()
        # 将进程对象添加进进程列表中
        p_list.append(p)
    # 遍历进程列表中的每一个子进程
    for p in p_list:
        # 主线程处于等的状态,子进程运行
        p.join()
# 4.开启多进程
if __name__ == '__main__':
    main()

(四)Process对象的其他方法或属性

(1)查看当前进程的进程号

(1)查看当前进程的进程号

(1)什么是进程号

  • 一个计算机上面运行着很多的进程,那么计算机是如何区分并管理这些进程服务端呢?
    • 计算机会给每一个运行的进程分配一个PID号

(2)如何查看进程号?

  • windows系统
    • CMD命令行 tasklist 查看
  • Mac系统
    • 终端运行 ps aux 查看

(3)如何根据指定进程号查看进程

  • Mac系统
    • 终端运行 ps aux|grep PID 即可查看
  • Windows系统
    • CMD 命令行 tasklist |findstr PID 即可查看

(2)查看当前进程的进程号方法

  • current_process().pid 方法 查看当前的进程号
# current_process().pid 方法
# 导入模块
from multiprocessing import Process,current_process
import time
def test(i):
    print(f"当前子进程{i}---进程号{current_process().pid}")
    time.sleep(3)

if __name__ == '__main__':
    print(f"这是当前进程的进程号{current_process().pid}")
    for i in range(1,3):
        p=Process(target=test,args=(i,))
        p.start()
# 这是当前进程的进程号17648
# 当前子进程2---进程号14660
# 当前子进程1---进程号11336

(3)查看当前进程的进程号

  • os.getpid() 方法 查看当前的进程号
# 导入模块
from multiprocessing import Process
import time
import os
def test(i):
    print(f"当前子进程{i}---进程号{os.getpid()}")
    time.sleep(3)

if __name__ == '__main__':
    print(f"这是当前进程的进程号{os.getpid()}")
    for i in range(1,3):
        p=Process(target=test,args=(i,))
        p.start()
# 这是当前进程的进程号21752
# 当前子进程1---进程号14652
# 当前子进程2---进程号22784

(4)查看当前进程的父进程号

  • os.getppid() 方法 查看当前的父进程号
# 导入模块
from multiprocessing import Process
import time
import os
def test():
    print(f"当前子进程的进程号{os.getpid()}")
    print(f"这是当前子进程的父进程号{os.getppid()}")
    time.sleep(3)

if __name__ == '__main__':
    print("开始")
    print(f"这是当前进程的进程号{os.getpid()}")
    print(f"这是当前进程的父进程号{os.getppid()}")
    p=Process(target=test)
    p.start()
# 开始
# 这是当前进程的进程号1184
# 这是当前进程的父进程号22768
# 当前子进程的进程号13880
# 这是当前子进程的父进程号1184

(2)杀死当前进程

  • p.terminate() 方法 杀死当前的进程
# 导入模块
from multiprocessing import  Process
import time
# 定义一个IO进程
def test(i):
    print(f"这是子进程{i}")
    time.sleep(3)
if __name__ == '__main__':
    for i in range(1,4):
        p=Process(target=test,args=(i,))
        p.start()
        # 定义杀死进程的条件
        if i == 2 :
            # 符合条件的话, 就杀死进程
            p.terminate()
#  这是子进程1
#  这是子进程3

(3)判断当前进程是否存活

  • p.is_alive() 方法 判断当前的进程是否存活
# ######杀死当前的进程
# 导入模块
from multiprocessing import  Process
import time
# 定义一个IO进程
def test():
    print(f"这是当前子进程的进程号{os.getpid()}")
    time.sleep(3)
if __name__ == '__main__':
        p=Process(target=test)
        p.start()
        # 杀死当前的进程 需要给操作系统一点时间!!!
        p.terminate()
        time.sleep(2)
        # 判断进程是否存活
        # 子程序被杀死!!!
        print(p.is_alive())
        # False
        print(f"主进程的进程号{os.getpid()}")
# 当前被杀死的是主进程的子进程
# 主进程正常
# False
# 主进程的进程号21192
  • 一般默认会将
  • 存储布尔值的变量名
  • 返回的结果是布尔值的方法名
  • 起成is_ 开头的变量名
posted @   苏苏!!  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示