351-进程

复习导航:直接看最下面的踩坑记录


进程(Process)

1.进程(Process)是资源分配的最小单位(CPU、内存、硬盘)
进程是操作系统中资源分配的基本单位,每个进程都有独立的内存空间,进程之间不能直接访问彼此的内存空间。
一个软件至少有一个进程,资源分配的最小单元,一个软件可能会有多个进程
2.多进程是Python程序中实现多任务的一种方式,使用多进程可以大大提高程序的执行效率。

多进程的作用
多进程的作用是提高程序的执行效率,通过多进程可以同时执行多个任务,从而提高程序的执行效率。

单进程,单任务的执行

import time

def music():
    for i in range(5):
        print("播放音乐")
        time.sleep(1)

def coding():
    for i in range(5):
        print("敲代码")
        time.sleep(1)

if __name__ == "__main__":
    music()
    coding()
    # 执行结果:
    # 播放音乐
    # 敲代码
    # 播放音乐
    # 敲代码
    # 播放音乐
    # 敲代码

多进程执行任务

程序运行会默认创建一个进程,这个默认创建的进程我们称之为主进程,程序运行后又创建了一个进程,我们称之为子进程

创建步骤:
1.导入multiprocessing模块:import multiprocessing
2.通过进程类创建进程对象:p = multiprocessing.Process(target=task, name="子进程名称", args=(参数1, 参数2, ...))
参数:
- target:指定进程执行的任务,即函数名
- name:指定进程的名称,一般不用设置
- group:指定进程组,一般不用设置(目前只能使用None,因为还没有实现这个组)
- args:指定任务的参数,以元组的形式传入,元组方式传参一定要和参数的顺序保持一致。
- kwargs:指定任务的参数,以字典的形式传入,字典方式传参字典中的key一定要和参数名保持一致。
3.启动进程执行任务:p.start()

import multiprocessing
import time

def music():
    for i in range(5):
        print("播放音乐")
        time.sleep(1)

def coding():
    for i in range(5):
        print("敲代码")
        time.sleep(1)

if __name__ == "__main__":
    # 在主进程中创建子进程
    p1 = multiprocessing.Process(target=music, name="播放音乐")
    p2 = multiprocessing.Process(target=coding, name="敲代码")
    # 启动子进程
    p1.start()
    p2.start()

tip:print()要执行两个操作:输出信息和换行操作,当多任务执行太快的时候,print()来不及换行,就会导致信息混在一起。

多进程执行带参数的任务

import multiprocessing
import time

def music(n):
    for i in range(n):
        print("播放音乐")
        time.sleep(1)

def coding(t):
    for i in range(3):
        print(f"敲代码{t}秒")
        time.sleep(t)

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=music, args=(3,))  # 传入参数:(3,),元组
    p2 = multiprocessing.Process(target=coding, args=(2,))
    p1.start()
    p2.start()

获取进程编号(PID)

1、进程编号的作用
当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程还有不同的子进程,那么就无法
进行有效的进程管理,为了方便管理实际上每个进程都是有自己编号的。
2、两种进程编号
①获取当前进程编号:os.getpid()
②获取当前进程的父进程:os.getppid()

杀掉进程

os.kill(进程编号, 信号)

随着信号不同,功能不同;

注意:多进程之间无法共享全局变量

子进程创建后,会复制父进程的资源,子进程之间无法共享全局变量。

举例子:单任务中多个函数之间可以共享全局变量

import time

my_list = []

def write():
    for i in range(5):
        my_list.append(i)
        time.sleep(0.1)
    print(my_list)

def read():
    print(my_list)

if __name__ == "__main__":
    write()
    time.sleep(1)
    read()
    # 执行结果:
    # [0, 1, 2, 3, 4]
    # [0, 1, 2, 3, 4]

多进程栗子:

import multiprocessing
import time

my_list = []

def write():
    for i in range(5):
        my_list.append(i)
        time.sleep(0.1)
    print(my_list)

def read():
    print(my_list)

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=write)
    p2 = multiprocessing.Process(target=read)
    p1.start()
    time.sleep(1)
    p2.start()

    # 执行结果:
    # [0, 1, 2, 3, 4]
    # []

当创建子进程的时候,全局变量会分别被复制一份独立存在,所以子进程之间无法共享全局变量。

注意:主进程与子进程的结束顺序

主进程默认会等待子进程执行结束以后再结束。

import multiprocessing
import time

def task():
    for i in range(3):
        print("任务执行中")
        time.sleep(1)

if __name__ == "__main__":
    p = multiprocessing.Process(target=task)
    p.start()
    time.sleep(1)
    print("主进程执行结束")

    # 执行结果:
    # 任务执行中
    # 主进程执行结束
    # 任务执行中
    # 任务执行中

进程代码执行结束后,整个程序并不会立即结束,而是等待子进程执行结束,当子进程执行结束后,整个主进程才能真正结束!
结论:主进程默认会等待子进程的结束而结束

关于设置主进程是否等待子进程执行完毕

要实现主进程代码结束,子进程代码一起跟着结束的方法

设置守护进程

销毁子进程


踩坑记录

在windows环境下面执行这个代码,无法正常运行:

from multiprocessing import Process
import time

# 定义在进程创建之前
def task():
    for i in range(4):
        print(i)
        time.sleep(1)

p  = Process(target=task)
p.start()

会报错

原因:

在 Windows 系统上,运行这个代码会遇到问题,因为在 Windows 上,使用 multiprocessing 模块时需要在 if __name__ == '__main__': 保护下启动进程。这是因为 Windows 使用的是 spawn 方法来创建子进程,它需要知道如何导入模块。

jupyter里中使用 multiprocessing 时,可能会遇到输出不显示

在 Jupyter Notebook 中使用 multiprocessing 时,可能会遇到输出不显示的问题。这是因为 Jupyter 使用的是不同的进程管理方式,导致标准输出的捕获和显示不如在命令行中那样直接。

线程和进程的对比在:350-多任务