多进程操作

多进程操作

  • multiprocessing 模块是 Python 标准库中用于支持多进程编程的模块。它提供了在多核和多处理器系统上创建和管理进程的工具,使得开发者能够更方便地进行并行和并发编程。

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__':

(1)直接使用Process方法

import multiprocessing


def run_task(i):
    print(f'当前的进程为:>>>{i}')


def main_task():
    for i in range(1, 10):
        # 创建进程对象
        task = multiprocessing.Process(target=run_task, args=(i,))
        # 启动进程
        task.start()


if __name__ == '__main__':
    main_task()

# 当前的进程为:>>>4
# 当前的进程为:>>>8
# 当前的进程为:>>>1
# 当前的进程为:>>>6
# 当前的进程为:>>>2
# 当前的进程为:>>>5
# 当前的进程为:>>>3
# 当前的进程为:>>>9
# 当前的进程为:>>>7

(2)继承Process类

import multiprocessing


def run_task(i):
    print(f'当前的进程为:>>>{i}')


def main_task():
    for i in range(1, 10):
        # 创建进程对象
        task = multiprocessing.Process(target=run_task, args=(i,))
        # 启动进程
        task.start()

# 第二种
class MyProcess(multiprocessing.Process):
    def __init__(self,i):
        super().__init__()
        self.i = i
    def run(self):
        print(f'当前是:>>>{self} | 参数为:>>>{self.i}')

def main_two():
    for i in range(1,10):
        task = MyProcess(i=i)
        task.start()


if __name__ == '__main__':
    main_two()

# 当前是:>>><MyProcess name='MyProcess-5' parent=18864 started> | 参数为:>>>5
# 当前是:>>><MyProcess name='MyProcess-3' parent=18864 started> | 参数为:>>>3
# 当前是:>>><MyProcess name='MyProcess-2' parent=18864 started> | 参数为:>>>2
# 当前是:>>><MyProcess name='MyProcess-6' parent=18864 started> | 参数为:>>>6
# 当前是:>>><MyProcess name='MyProcess-1' parent=18864 started> | 参数为:>>>1
# 当前是:>>><MyProcess name='MyProcess-8' parent=18864 started> | 参数为:>>>8
# 当前是:>>><MyProcess name='MyProcess-4' parent=18864 started> | 参数为:>>>4
# 当前是:>>><MyProcess name='MyProcess-7' parent=18864 started> | 参数为:>>>7
# 当前是:>>><MyProcess name='MyProcess-9' parent=18864 started> | 参数为:>>>9

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

  • 每一个子进程之间的数据是相互隔离的
  • 在执行子进程代码时,只修改自己子进程内的数据,不会影响到其他的子进程
import multiprocessing

number = 99


def run_task(i):
    global number
    number += 1
    print(f'这是子进程{i}:>>>{number}')


def main_one():
    for i in range(1, 5):
        task = multiprocessing.Process(target=run_task, args=(i,))
        task.start()


if __name__ == '__main__':
    main_one()
# 这是子进程1:>>>100
# 这是子进程2:>>>100
# 这是子进程4:>>>100
# 这是子进程3:>>>100

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

(1)服务端

from socket import *
from multiprocessing import Process

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)

def talk(conn,client_addr):
    while True:
        try:
            msg=conn.recv(1024)
            if not msg:break
            conn.send(msg.upper())
        except Exception:
            break

if __name__ == '__main__': #windows下start进程一定要写到这下面
    while True:
        conn,client_addr=server.accept()
        p=Process(target=talk,args=(conn,client_addr))
        p.start()

(2)客户端

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))


while True:
    msg=input('>>: ').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    msg=client.recv(1024)
    print(msg.decode('utf-8'))

Process对象的join方法

  • 在Python中,Process对象的join()方法用于等待子进程结束。主要作用是在主程序中调用join()方法,使得主程序等待所有的子进程执行完成后再继续执行

  • 将并行转为串行

(1)非join方法 并行

  • 在这个示例中,由于没有使用p.join(),主程序不会等待子进程完成,因此主程序可能会在子进程还在运行时就结束。

  • 在这里,主程序会立即输出"这是进程的主程序",而三个子进程会并发地执行各自的task函数。总的运行时间由主程序开始执行到所有子进程执行完毕的时间之和。

from multiprocessing import Process
import time


# 定义任务函数,模拟每个进程需要执行的工作
def task(task_name, task_time):
    print(f'这是进程:>>>>{task_name} 需要运行:>>>>{task_time}s')

    # 模拟任务执行时间
    time.sleep(task_time)

    print(f'这是进程:>>>>{task_name} 执行完毕')


if __name__ == '__main__':
    # 初始化进程列表
    p_list = []

    # (1)创建进程对象
    # Process(target=被调用的任务名(进程名), args=(参数,))
    # args:里面的参数一定要用逗号隔开(容器类型无论里面有几个元素,哪怕只有一个元素,也一定要用逗号隔开)
    for i in range(1, 4):
        p = Process(target=task, args=(i, i,))
        p_list.append(p)

    start_time = time.time()

    # (2)开启进程
    # 告诉操作系统帮我们创建一个进程
    for p in p_list:
        p.start()

    # 主程序不等待子进程 p 运行结束后再继续往后执行

    print(f'这是进程的主程序')

    # (3)输出程序运行的总耗时
    print(f'这是主程序运行的总耗时:{time.time() - start_time}')

# 这是进程的主程序
# 这是主程序运行的总耗时:0.008986949920654297
# 这是进程:>>>>3 需要运行:>>>>3s
# 这是进程:>>>>1 需要运行:>>>>1s
# 这是进程:>>>>2 需要运行:>>>>2s
# 这是进程:>>>>1 执行完毕
# 这是进程:>>>>2 执行完毕
# 这是进程:>>>>3 执行完毕

(2)join方法 串行

  • 在启动每个子进程后立即调用了p.join(),这样的话主程序会在每个子进程启动后立即等待该子进程执行完毕,然后再启动下一个子进程。这导致了串行执行的效果,而不是并行。
from multiprocessing import Process
import time


def task(task_name, task_time):
    print(f'这是进程:>>>>{task_name} 需要运行:>>>>{task_time}s')
    time.sleep(task_time)
    print(f'这是进程:>>>>{task_name} 执行完毕')


if __name__ == '__main__':
    # 初始化进行列表
    p_list = []

    # (1)创建进程对象
    # Process(target=被调用的任务名(进程名), args=(参数,))
    # args:里面的参数一定要用逗号隔开(容器类型无论里面有几个元素,哪怕只有一个元素,也一定要用逗号隔开)
    for i in range(1, 4):
        p = Process(target=task, args=(i, i,))
        p_list.append(p)

    start_time = time.time()
    # (2)开启进程
    # 告诉操作系统帮我们创建一个进程

    for p in p_list:
        p.start()
        p.join()
    print(f'这是进程的主程序')
    print(f'这是程序运行的总耗时:{time.time() - start_time}')

# 这是进程:>>>>1 需要运行:>>>>1s
# 这是进程:>>>>1 执行完毕
# 这是进程:>>>>2 需要运行:>>>>2s
# 这是进程:>>>>2 执行完毕
# 这是进程:>>>>3 需要运行:>>>>3s
# 这是进程:>>>>3 执行完毕
# 这是进程的主程序
# 这是程序运行的总耗时:7.042375564575195

(3)join方法 并行

from multiprocessing import Process
import time


# 定义任务函数,模拟每个进程需要执行的工作
def task(task_name, task_time):
    print(f'这是进程:>>>>{task_name} 需要运行:>>>>{task_time}s')

    # 模拟任务执行时间
    time.sleep(task_time)

    print(f'这是进程:>>>>{task_name} 执行完毕')


if __name__ == '__main__':
    # 初始化进程列表
    p_list = []

    # (1)创建进程对象
    # Process(target=被调用的任务名(进程名), args=(参数,))
    # args:里面的参数一定要用逗号隔开(容器类型无论里面有几个元素,哪怕只有一个元素,也一定要用逗号隔开)
    for i in range(1, 4):
        p = Process(target=task, args=(i, i,))
        p_list.append(p)

    # 记录程序开始执行的时间
    start_time = time.time()

    # 初始化任务列表
    task_list = []

    # (2)开启进程
    # 告诉操作系统帮我们创建一个进程
    for p in p_list:
        p.start()
        task_list.append(p)

    # (3)主程序等待所有子进程 p 运行结束后再继续往后执行
    for task in task_list:
        task.join()

    # 主程序继续执行,输出主程序结束的信息
    print(f'这是进程的主程序')

    # 输出程序运行的总耗时
    print(f'这是程序运行的总耗时:{time.time() - start_time}')

# 这是进程:>>>>2 需要运行:>>>>2s
# 这是进程:>>>>3 需要运行:>>>>3s
# 这是进程:>>>>1 需要运行:>>>>1s
# 这是进程:>>>>1 执行完毕
# 这是进程:>>>>2 执行完毕
# 这是进程:>>>>3 执行完毕
# 这是进程的主程序
# 这是程序运行的总耗时:3.4535300731658936

Process对象的其他方法或属性

(1)什么是进程号

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

(2)如何查看进程号

  • Windows系统
    • CMD 命令行 tasklist 即可查看进程号

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

  • Windows系统
    • CMD 命令行 tasklist |findstr PID 即可查看

(4)查看当前进程的进程号current_process().pid 方法

from multiprocessing import Process, current_process
import time


def task():
    # 查看当前进程的 进程(PID) 号
    print(f'当前程序:>>>>{current_process().pid} 正在运行')
    time.sleep(2)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()

    print(f'这是主程序:>>>{current_process().pid}')
# 这是主程序:>>>15372
# 当前程序:>>>>6704 正在运行

(5)查看当前进程的进程号os.getpid() 方法

from multiprocessing import Process
import os
import time


def task():
    # 查看当前进程的 进程(PID) 号
    print(f'当前程序:>>>>{os.getpid()} 正在运行')
    time.sleep(2)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()

    print(f'这是主程序:>>>{os.getpid()}')

# 这是主程序:>>>18276
# 当前程序:>>>>9992 正在运行

(6)查看当前进程的父进程的进程号os.getppid() 方法

from multiprocessing import Process
import os
import time


def task():
    # 查看当前进程的 进程(PID) 号
    print(f'当前程序:>>>>{os.getpid()} 正在运行')
    # 查看当前进程的 父进程(PID) 号
    print(f'当前程序的父进程:>>>>{os.getppid()} 正在运行')
    time.sleep(2)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()

    print(f'这是主程序:>>>{os.getpid()}')
    print(f'这是主程序的父进程:>>>{os.getppid()}')

    # 这是主程序:>>>19344
    # 这是主程序的父进程:>>>13344
    # 当前程序:>>>>3996 正在运行
    # 当前程序的父进程:>>>>19344 正在运行

(7)杀死当前进程p.terminate()

from multiprocessing import Process
import time


def task():
    print("子进程开始执行")
    time.sleep(5)
    print("子进程执行完毕")


if __name__ == '__main__':
    # 创建进程对象
    p = Process(target=task)

    # 启动进程
    p.start()

    # 模拟主程序执行其他任务
    time.sleep(2)

    # 强制终止进程
    p.terminate()

    print("主程序执行完毕")
    
# 子进程开始执行
# 主程序执行完毕

(8)判断当前进程是否存活p.is_alive()

from multiprocessing import Process
import time


def task():
    print("子进程开始执行")
    time.sleep(5)
    print("子进程执行完毕")


if __name__ == '__main__':
    # 创建进程对象
    p = Process(target=task)

    # 启动进程
    p.start()

    # 检查进程是否存活
    while p.is_alive():
        print("主程序:子进程还在执行...")
        time.sleep(1)

    print("主程序:子进程已经结束")
    
# 主程序:子进程还在执行...
# 子进程开始执行
# 主程序:子进程还在执行...
# 主程序:子进程还在执行...
# 主程序:子进程还在执行...
# 主程序:子进程还在执行...
# 主程序:子进程还在执行...
# 子进程执行完毕
# 主程序:子进程已经结束
posted @ 2024-01-17 16:49  ssrheart  阅读(3)  评论(0编辑  收藏  举报