Python 测开28期 - WL - 学习笔记 - 多任务进程编程

多任务进程编程

进程实现多任务

定义

  • 一个正在运行的程序或者软件就是一个进程
  • 是操作系统进行资源分配的基本单位

python 使用的模块

  • import multiprocessing

创建进程

  • multiprocessing 模块使用 Process 类创建进程实例对象,实现进程任务的创建
  • Process([group [, target [, name [, args [, kwargs]]]]])

参数说明

  • group:制定进程组,目前只能使用 None
  • target:执行的任务目标名
  • name:进程名字
  • args:以元组方式给执行任务传参
  • kwargs:以字典方式给执行任务传参

示例

import multiprocessing as mu
import time

# 跳舞任务
def task1():
    for i in range(10):
        print('task 1 run....')
        print(time.time())
        time.sleep(0.3)

# 唱歌任务
def task2():
    for i in range(10):
        print('task 2 run....')
        print(time.time())
        time.sleep(0.3)

# 程序入口
if __name__ == '__main__':
    p1 = mu.Process(target=task1, name='My process1')
    p2 = mu.Process(target=task2)

启动进程

  • 进程对象创建成功后,需要启动才会执行
  • p1.start()
  • p2.start()

获取当前进程

  • multiprocessing.current_process() 可以获取当前进程
def task1():
    for i in range(3):
        print('task 1 run....  ,获取当前进程', mu.current_process())
        # task 1 run....  ,获取当前进程 <Process name='My process1' parent=12672 started>
        time.sleep(0.3)

# 唱歌任务
def task2():
    for i in range(3):
        print('task 2 run....  ,获取当前进程', mu.current_process())
        # task 2 run....  ,获取当前进程 <Process name='Process-4' parent=12672 started>
        time.sleep(0.3)

获取进程名

  • 进程对象的 name 属性可以获取进程的名称
def task1():
    for i in range(3):
        print('task 1 run....  ,获取当前进程', mu.current_process().name)
        # task 1 run....  ,获取当前进程 My process1
        time.sleep(0.3)

# 唱歌任务
def task2():
    for i in range(3):
        print('task 2 run....  ,获取当前进程', mu.current_process().name)
        # task 2 run....  ,获取当前进程 Process-4
        time.sleep(0.3) 

获取进程 ID

  • 每一个进程产生时,操作系统都会为进程分配一个 ID 编号,可以通过 os 模块中的方法获取进程的 ID
  • os.getpid() 获取当前进程 ID
  • os.getooid() 获取当前进程的父进程的 ID
def task1():
    for i in range(2):
        print(f'task 1  run...., 获取当前进程, {mu.current_process().name}_ID',os.getpid())
        print(f'task 1  run...., 获取当前进程, {mu.current_process().name}_Parent_ID',os.getppid())
        # task 1  run...., 获取当前进程, Process-1_ID 3916
        # task 1  run...., 获取当前进程, Process-1_Parent_ID 1228
        time.sleep(0.3)

# 唱歌任务
def task2():
    for i in range(2):
        print(f'task 2  run...., 获取当前进程, {mu.current_process().name}_ID',os.getpid())
        print(f'task 2  run...., 获取当前进程, {mu.current_process().name}_Parent_ID',os.getppid())
        # task 2  run...., 获取当前进程, Process-2_ID 12668
        # task 2  run...., 获取当前进程, Process-2_Parent_ID 13380
        time.sleep(0.3)

进程任务函数传参

  • 在创建进程对象的时候,为进程任务函数传递参数,可以使用两种方法
  • args:使用可变位置参数形式传参
  • kwargs:使用可变关键字参数形式传参
def task1(n, msg):
    for i in range(n):
        print(mu.current_process().name, f'task1--打印第{i + 1} 次 {msg}')
        time.sleep(0.3)
def creat_task():
    p1 = mu.Process(target=task1, args=(3, 'tasssssk1'))
    # 启动进程
    p1.start()
    print(f'{mu.current_process()}_父级_ID', os.getpid())
# 程序入口
if __name__ == '__main__':
    creat_task()
# Process-1 task1--打印第1 次 tasssssk1
# Process-1 task1--打印第2 次 tasssssk1
# Process-1 task1--打印第3 次 tasssssk1

进程同步

  • join() 方法用来将子进程添加到当前进程之前执行,直到子进程执行结束后,当前进程才会继续执行
  • 多个进程间的代码运行时是交替执行的,如果使用 join() 方法后,当前进程会进入到阻塞状态,等待子进程执行完毕后才会解除阻塞状态,继续执行
  • 使用 join() 方法后,可以使多进程的 异步执行 变成 同步执行 ,过多使用会使程序效率变低
def task1(n, msg):
    for i in range(n):
        print(mu.current_process().name, f'task1--打印第{i + 1} 次 {msg}')
        time.sleep(0.3)
def creat_task():
    p1 = mu.Process(target=task1, args=(3, 'tasssssk1'))
    # 启动进程
    p1.start()
    p1.join()
    print(f'执行父级任务,{mu.current_process()}_父级_ID', os.getpid())
# 程序入口
if __name__ == '__main__':
    creat_task()
# Process-1 task1--打印第1 次 tasssssk1
# Process-1 task1--打印第2 次 tasssssk1
# Process-1 task1--打印第3 次 tasssssk1
# 执行父级任务,<_MainProcess name='MainProcess' parent=None started>_父级_ID 996

守护进程

  • 多进程在执行时,父进程会等待子进程执行结束才会结束
  • 如果需要子进程在父进程执行结束后就结束执行,无论子进程是否执行完毕,可以将子进程设置为守护进程
  • 使用 子进程对象.daemon = Ture 在子进程启动前将子进程设置为守护进程
  • 使用 子进程对象.terminate() 在主进程退出前手动将子进程结束
设置子进程为守护进程
import multiprocessing as mu
import os
import time


# 跳舞任务
def task1(n, msg):
    for i in range(n):
        print(mu.current_process().name, f'task1--打印第{i + 1} 次 {msg}')
        time.sleep(0.3)


def task2(n, msg):
    for i in range(n):
        print(mu.current_process().name, f'task2--打印第{i + 1} 次 {msg}')
        time.sleep(0.3)


def creat_task():
    p1 = mu.Process(target=task1, args=(300, 'tasssssk1'))
    p2 = mu.Process(target=task1, kwargs={"n": 20, "msg": "taaaaaask2"})
    # 启动进程
    print('父级任务开始')
    print(f'执行父级任务,{mu.current_process()}_父级_ID', os.getpid())
    # 将子进程设置为守护进程
    p1.daemon = True
    p2.daemon = True
    p1.start()
    p2.start()
    time.sleep(0.5)
    print("父级任务结束")


# 程序入口
if __name__ == '__main__':
    creat_task()
# 父级任务开始
# 执行父级任务,<_MainProcess name='MainProcess' parent=None started>_父级_ID 9028
# Process-1 task1--打印第1 次 tasssssk1
# Process-2 task1--打印第1 次 taaaaaask2
# Process-1 task1--打印第2 次 tasssssk1
# Process-2 task1--打印第2 次 taaaaaask2
# 父级任务结束

手动杀死子进程
def creat_task():
    p1 = mu.Process(target=task1, args=(300, 'tasssssk1'))
    p2 = mu.Process(target=task1, kwargs={"n": 20, "msg": "taaaaaask2"})
    # 启动进程
    print('父级任务开始')
    print(f'执行父级任务,{mu.current_process()}_父级_ID', os.getpid())
    # 将子进程设置为守护进程
    # p1.daemon = True
    # p2.daemon = True
    p1.start()
    p2.start()
    time.sleep(0.5)
    # 手动杀死子进程实现守护效果
    p1.terminate()
    p2.terminate()
    print("父级任务结束")


# 程序入口
if __name__ == '__main__':
    creat_task()
# 父级任务开始
# 执行父级任务,<_MainProcess name='MainProcess' parent=None started>_父级_ID 7512
# Process-1 task1--打印第1 次 tasssssk1
# Process-2 task1--打印第1 次 taaaaaask2
# Process-1 task1--打印第2 次 tasssssk1
# Process-2 task1--打印第2 次 taaaaaask2
# 父级任务结束

进程间不共享全局变量

  • 因为进程是程序执行的最小资源分配单位,当一个子进程被创建时,子进程会复制父进程的资源,形成一个独立的空间,所以多个子进程之间的数据是独立不共享的
import multiprocessing as mu
import os
import time

# 定义全局变量
g_list = list()

# 添加数据的任务
def add_data():
    for i in range(3):
        g_list.append(i)
        print("add:",i)
        time.sleep(0.2)
    print("add_data:", g_list)
# add: 0
# add: 1
# add: 2
# add_data: [0, 1, 2]
def read_data():
    print("read_data:",g_list)
# read_data: []
def creat_data():
    p1 = mu.Process(target=add_data)
    p2 = mu.Process(target=read_data)
    p1.start()
    p1.join()
    p2.start()
    print('creat_data:',g_list)
# creat_data: []

if __name__ == '__main__':
    creat_data()

进程相关操作