【百尺竿头,更进一步学Python】Python进阶课程——Python进程
通过上篇博客我们知道,每个任务可以视为一个应用程序,每一个运行中的程序是一个进程,即进程是应用程序的执行实例.上篇我们把多任务进行了详细的讲解,今天我们就来讲一下什么是进程.
进程
进程的介绍
- 在Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式
进程的概念
-
一个正在运行的程序或者软件就是一个进程
-
进程是操作系统进行资源分配的最基本单位
-
每次启动一个软件(进程)操作系统都会给其分配一定的运行资源(内存资源)保证其正常工作运行
举个栗子,让大家更加形象的去理解进程.
- 生活中的工厂(计算机),工厂中的每一个车间就是一个进程,工厂负责提供资源(占地面积、电力设备等),真正能够干活的是车间员工,员工可以理解为线程(线程在下篇博客会讲到)
总结:
- 一个程序运行至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程
进程的分类
单进程
-
默认程序运行创建一个进程
-
一个Python文件运行,就是开启一个进程去处理
多进程
-
一个Python文件运行,占用一个进程去处理,假如同时要运行第二个Python文件,同样给第二个Python文件开启一个进程去处理
-
多进程可以完成多任务,每个进程就好比一个独立车间,每个车间都各自在运营,每个进程也是各自在运行,执行各自的任务
多进程的使用
- 在使用多进程之前,必须要先导入进程包
import multiprocessing
实例:
# 导入进程包
import multiprocessing
import time
def func():
for i in range(3):
print("跑步")
time.sleep(1)
def fun():
for j in range(3):
print("唱歌")
time.sleep(1)
# 按照以前,程序会从上到下顺序执行
# func()
# fun()
# 执行结果是:
跑步
跑步
跑步
唱歌
唱歌
唱歌
# 学习了多进程以后,我们可以一边跑步一边唱歌
# 使用process进程类参数
# target:执行的目标任务名称
func_process = multiprocessing.Process(target=func)
fun_process = multiprocessing.Process(target=fun)
# start():启动子进程实例(启动创建子进程)
func_process.start()
fun_process.start()
执行结果是:
唱歌
跑步
唱歌
跑步
唱歌
跑步
Process进程类的说明
Process进程类的格式:
func_process = multiprocessing.Process([group,target,name,args,kwargs])
参数:
- group:指定进程组,目前只能使用None
- target:执行的目标任务名称
- name:当前进程的名字,默认为Process-N,N为从1开始的递增整数
- args:以元祖方式给执行的任务传递参数
- kwargs:以字典方式给执行的任务传递参数
实例:
import time
import multiprocessing
def sing(num):
for i in range(1, num):
print(f"小红在唱第{i}首歌!")
time.sleep(1)
def run(num, str):
for i in range(1, num):
print(f"{str}跑了第{i}圈了!")
time.sleep(1)
# kwargs:以字典方式给执行的人物传递参数
sing_process = multiprocessing.Process(group=None, target=sing, name="sing", kwargs={"num": 4})
print(sing_process.name)
# args:以元组方式给执行的人物传递参数
run_process = multiprocessing.Process(target=run, args=(4, "小明"))
print(run_process.name)
sing_process.start()
run_process.start()
执行的结果:
sing
Process-2
小明跑了第1圈了!
小红在唱第1首歌!
小明跑了第2圈了!
小红在唱第2首歌!
小明跑了第3圈了!
小红在唱第3首歌!
Process创建的实例对象常用的方法
- start():启动子进程实例(启动创建子进程)
- join():等待子进程执行结束,相当于给这个进程做了一个单独检测的功能,当这个子进程执行完毕之后主进程才继续往后执行
- terminate():不管任务是否完成,立即终止子进程
实例:
# 导入进程包
import multiprocessing
import time
def func():
for i in range(3):
print("跑步")
time.sleep(1)
def fun():
for j in range(3):
print("唱歌")
time.sleep(1)
func_process = multiprocessing.Process(target=func)
fun_process = multiprocessing.Process(target=fun)
func_process.start()
# func_process.join()
# 使用join以后执行结果是:
# 跑步
# 跑步
# 跑步
# 唱歌
# 唱歌
# 唱歌
fun_process.start()
func_process.terminate()
# 使用terminate以后的执行结果是:
# 唱歌
# 唱歌
# 唱歌
# 因为在开始以前,我们把func_process子进程终止了,所以最后的结果没有跑步,只有唱歌。
获取进程编号
获取进程编号的目的
- 获取进程的编号的目的是验证主进程和子进程的关系,可以得知子进程是由哪个主进程创建出来的
获取进程编号的方式
-
获取当前进程的编号
使用os.getpid()获取当前进程的编号
-
获取当前父进程的编号
扫描二维码关注公众号,回复: 11637667 查看本文章使用os.getppid()获取当前父进程的编号
-
multiprocessing.current_process()方法获取当前进程的详细信息(进程名称和进程编号)
注意:
- 所有的子进程都来自于父进程,因此一个程序中的主进程编号得知以后,子进程编号按照主进程编号为起始值加一计算就可以得出子进程的编号.
实例:
# 导入进程包
import multiprocessing
import time
import os
def func():
print(f"func的进进程编号是:{os.getpid()}")
print(f"func父进程的编号是:{os.getppid()}")
print(f"func当前进程的详细信息:{multiprocessing.current_process()}")
for i in range(3):
print("跑步")
time.sleep(1)
def fun():
print(f"fun的进进程编号是:{os.getpid()}")
print(f"fun父进程的编号是:{os.getppid()}")
print(f"fun当前进程的详细信息:{multiprocessing.current_process()}")
for j in range(3):
print("唱歌")
time.sleep(1)
func_process = multiprocessing.Process(target=func)
fun_process = multiprocessing.Process(target=fun)
func_process.start()
fun_process.start()
print(f"主进程的编号是:{os.getpid()}")
print(f"当前主进程的详细信息:{multiprocessing.current_process()}")
# 执行的最后的结果是:
# func的进进程编号是:3662
# func父进程的编号是:3661
# func当前进程的详细信息:<Process name='Process-1' parent=3661 started>
# 跑步
# 主进程的编号是:3661
# 当前主进程的详细信息:<_MainProcess name='MainProcess' parent=None started>
# fun的进进程编号是:3663
# fun父进程的编号是:3661
# fun当前进程的详细信息:<Process name='Process-2' parent=3661 started>
# 唱歌
# 跑步
# 唱歌
# 跑步
# 唱歌
Process中的kill方法
实例:
# 导入进程包
import multiprocessing
import time
import os
def func():
for i in range(3):
print("跑步")
time.sleep(1)
def fun():
for j in range(3):
print("唱歌")
if (j == 1):
# 通过指定的进程编号杀死进程,9可以理解为是向这个进程发送的命令编号为9(直接强制终止进程)
os.kill(os.getpid(), 9)
time.sleep(1)
func_process = multiprocessing.Process(target=func)
fun_process = multiprocessing.Process(target=fun)
func_process.start()
fun_process.start()
# 最后执行的结果是:
# 跑步
# 唱歌
# 跑步
# 唱歌
# 跑步
# 因为当唱歌执行到一的时候,使用kill命令杀死了子进程,所以最后一次唱歌没有执行
进程的特性
进程特性介绍
- 进程之间不共享全局变量
- 主进程会等待所有的子进程结束之后再结束
进程之间不共享全局变量
- 当一个进程对全局变量进行数据的修改时,对于其他进程而言不会造成任何的影响.
- 可以理解为每个进程都拿的是最初始的全局变量。
- 全局变量就是所谓资源,当创建一个进程,则系统会直接给这个进程里面复制一个全局变量。针对于这个全局变量而言,在进程之间都是相互独立存在的,之间没有任何的联系
- 进程之间不共享全局变量。他们之间的关系只有一点,不同进程之间的全局变量的名字相同罢了
实例:
# 导入进程包
import multiprocessing
import time
value = []
def func():
for i in range(1, 6):
value.append(i)
time.sleep(1)
print(value)
def fun():
for j in range(6, 11):
value.append(j)
time.sleep(1)
print(value)
func_process = multiprocessing.Process(target=func)
fun_process = multiprocessing.Process(target=fun)
func_process.start()
fun_process.start()
print(value)
# 最后执行的结果是:
# []
# [1, 2, 3, 4, 5]
# [6, 7, 8, 9, 10]
# 由此可以证明进程之间不共享全局变量
主进程会等待所有的子进程结束之后再结束
-
在一个含有子进程的Python程序中,主进程会等待所有的子进程执行结束之后才能结束
-
在主进程结束之前,手动结束了所有的子进程,那么程序的结束由主进程的结束来控制
如果希望主进程提前结束,有以下两种方式:
-
在主进程结束之前,保证所有子进程结束使用子进程的terminate().
terminate()函数的实例在上面(Process创建的实例对象常用的方法)中有写到. -
如果有一个进程必须设置为无限循环,那么该进程就永远不会结束,意味着整个python程序就永远不能结束,那为了能够让python程序正常退出,将这类无限循环的进程设置为守护进程,当程序当中仅仅剩下守护线程时,python程序就能够正常退出,不必关心这类线程是否执行完毕,这就是守护线程
用法:在子进程开启之前,设置当前子进程被被主进程守护,子进程的deamon属性设为true则意味着这个子进程被主进程守护,主进程结束守护结束,子进程也结束
实例:
# 导入进程包
import multiprocessing
import time
def func():
for i in range(999999999):
print(i)
time.sleep(0.5)
# 主进程
if __name__ == '__main__':
func_process = multiprocessing.Process(target=func)
# 把func设置为守护进程
func_process.daemon = True
func_process.start()
# 主进程设置延迟五秒以后结束
# 因为上面我们设置了func是守护进程,所以当主进程结束以后,被守护的进程func也相应的结束
time.sleep(5)
print("主进程结束了")
# 最后的执行结果是:
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 主进程结束了
# 本来func应该是一直输出到999999999但是我们使用deamon函数以后,主进程结束,相应的被守护的子进程也结束了。