多任务编程
意义:充分利用计算机的资源提高程序的运行效率
定义:通过应用程序利用计算机多个核心,达到同时执行多个任务的目的
实施方案: 多进程 多线程
并行:多个计算机核心在同时处理多个任务,多个任务之间是并行关系。
并发:计算机同时处理多个任务,内核在多个任务间不断切换,达到好像在同时处理的运行效果。同时多个任务实际为并发关系。
进程:程序在计算机中运行一次的过程
程序:是一个可执行文件,是静态的,占有磁盘,不占有计算机运行资源。
进程:进程是一个动态的过程描述,占有cpu内存等计算机资源的,有一定的生命周期。
* 同一个程序的不同执行过程是不同的进程,因为分配的计算机资源等不同。
进程的创建流程:
1、用户空间运行一个程序,发起进程创建
2、操作系统接受用户请求,开启进程创建。
3、操作系统分配系统资源,确认进程状态。
4、将创建好的进程提供给应用层使用。
cpu时间片:
如果一个进程占有计算机核心,我们称为该进程在cpu时间片上。多个任务实际对cpu会进行争夺,一般由操作系统分配cpu时间片。
PCB(进程控制块)
在操作系统中,进程创建后会自动产生一个空间存放进程信息,称为进程控制块。
进程信息: 进程PID 进程占有的内存位置,创建时间,用户
进程PID:进程在操作系统中的唯一编号,大于0的整数,由系统自动分配。
进程信息查看命令:linux ps -aux windows:netstat -ano
进程特征:
1、进程是操作系统分配计算机资源的最小单位
2、每个进程都有子集单独的虚拟内存空间
3、进程间的执行相互独立,互不影响。
进程的状态:
三态:
*就绪态:进程具备执行条件,等待系统分配cpu
*运行态:进程占有cpu处理器,处于运行状态
*等待态:进程暂时不具备运行条件,需要阻塞等待
五态:(增加了新建态和终止态)
*新建态: 创建一个新的进程,获取资源的过程
*终止态:进程结束释放资源的过程。
linux中:ps -aux ---》stat查看进程状态
D 等待态 不可中断等待
S 等待态 可中断等待
T 等待态 暂停状态
R 运行态 就绪态 运行态
Z 僵尸态 进程已经结束,但是存在遗留问题。
+ 前台进程
< 高优先级
N 低优先级
l 有进程链接
s 会话组组长
进程优先级
优先级决定了一个进程的执行权限和占有资源的优先程度。
查看进程优先级:
top:动态的查看进程优先级
取值范围: -20 —— 19 -20 优先级最高。
nice :指定优先级运行程序。
eg:终端 nice -9 程序.py
父子进程:
在系统中除了初始化进程之外,每个进程都有一个父进程,可能有0个或者多个子进程。由此形成父子进程关系,我们认为每个进程都是父进程发起请求创建的。LINUX
进程(process)
查看进程树: pstree(linux)
查看父进程PID: ps -ajx
import os
from time import sleep
#fork之前的部分只有父进程执行
print("hello world!")
#创建一个新的进程,无参数,
#返回值:失败返回 -1,成功 在原有进程中返回新的进程PID
# 在新的进程中返回0
pid = os.fork()
if pid < 0:
print("进程创建失败。")
elif pid == 0:
print("新创建的进程。")
else:
sleep(1) #让父进程进入等待态,这样就可以让子进程先进行。
print("原来的进程。")
print("程序执行完毕。")
运行结果:
创建失败: 进程创建失败
程序执行完毕
创建成功:(执行的顺序不一定,谁先抢到谁先开始)
原来的进程
程序执行完成
新创建的进程
程序执行完毕
*子进程会复制父进程全部代码段,包括fork前的代码
*子进程从fork的下一句开始执行
*父子进程通常会根据fork返回值的差异选择执行不同的代码(使用if结构)
*父子进程在执行上互不干扰,执行顺序不确定。
*子进程虽然复制父进程内存空间,但是有子集的特性,比如PID号,PCB等。
*父子进程空间独立,各自修改各自的内容,互不影响。
进程相关函数使用:
os.getpid()
功能:获得当前进程的pid号
返回值: 返回PID号
os.getppid()
功能:获取父进程的进程号
返回值:返回PID号
import os
pid = os.fork()
#仔细看不同位置的ID是不同的参数。
if pid < 0:
print("Create Process failed")
elif pid == 0:
print("子进程PID",os.getpid())
print("父进程PID",os.getppid())
else:
print("父进程PID",os.getpid())
print("子进程PID",pid())
os._exit(status)
功能:推出进程
参数:进程的推出状态 整数
import os
#进程结束
os._exit(0)
print("hello")
什么也不会打印,因为直接退出了。
sys.exit([status])
功能:退出进程
参数:默认为0 如果是整数则表示退出状态,如果是字符串字退出时打印字符串。
sys.exit 可以通过捕获异常SystemExit 阻止退出
孤儿进程: 父进程先于子进程退出,此时子进程就会成为孤儿进程。
*孤儿进程会被系统指定的进程收养,即系统进程会成为该孤儿进程新的父进程。
*孤儿进程退出时该父进程会处理退出状态。
import os
from time import sleep
pid = fork()
if pid < 0:
print("create process failed")
elif pid == 0:
print("父进程pid:",os.getppid())
sleep(2)
print("父进程pid:",os.getppid())
else:
sleep(1)
print("parent PID:",os.getpid())
sys.exit("父进程退出")
运行结果:
父进程pid: 10965
Parent PID:10965
父进程退出
父进程PID:1778 #重新找父进程
僵尸进程: 子进程先于父进程退出,父进程没有处理子进程退出状态,此时子进程称为僵尸进程。
*僵尸进程已经结束,但是会滞留部分PCB信息在内存,大量的僵尸会消耗系统资源,应该尽量避免。
#僵尸进程演示
import os,sys
from time import sleep
pid = os.fork()
if pid < 0:
print("create process failed")
elif pid == 0:
print("子进程PID:",os.getpid())
sys.exit("子进程退出")
else:
sleep(1)
while True:
pass
如何避免僵尸进程产生
* 让父进程先退出
创建二级子进程:
1、父进程创建子进程等待子进程退出
2、子进程创建二级子进程,然后马上退出
3、二级子进程成为孤儿,处理具体事件
#创建二级子进程防止僵尸。
import os
from time import sleep
def fun1():
sleep(3)
print("第一件事情")
def fun2():
sleep(4)
print("第二件事情")
pid = os.fork()
if pid < 0:
print("Create process error")
elif pid == 0:
#创建二级进程
pid0 = os.fork()
if pid0 < 0:
print("创建二级进程失败")
elif pid0 == 0:
fun2() #一级子进程创建的二级子进程,一级退出后,会成为孤儿。
else:
os._exit(0) #一级子进程退出,会有父进程等待处理。
else:
os.wait() #父进程等待处理。
fun1()
运行结果:
第一家事情
第二件事情
第一件事情
第二件事情
*父进程处理子进程退出状态
pid,status = os.wait()
功能:在父进程中阻塞等待处理子进程的退出
返回值: pid 退出的子进程的PID号
status 子进程的退出状态
pid,status = os.waitpid(pid,option)
功能:同wait
参数:pid -1 表示任意子进程退出
>0 整数 指定PID号的子进程退出
option 0 表示阻塞等待
WNOHONG 表示非阻塞
返回值:同wait
waitpid(-1,0)等价于 wait()
#如何防止僵尸进程产生。
import os,sys
from time import sleep
pid = os.fork()
if pid < 0:
print("create process failed")
elif pid == 0:
sleep(3)
print("子进程PID:",os.getpid())
sys.exit("子进程退出")
else:
#等待子进程退出
while True:
sleep(1)
pid,status = os.waitpid(-1,os.WNOHONG)
print(pid,status)#status会乘以256
if os.WEXITSTATUS(status):#获取未乘以256的退出状态
break
print("do something others")
multiprocessing 模块创建进程
1、需要将要做的事情封装为函数。
2、使用lultiprocessing中提供的process类创建进程对象
3、通过进程兑现格合Process初始化函数 对进程进行设置,并且绑定要执行的事件。
4、启动进程,会自动执行先关联函数。
5、事件完成后回收进程
创建进程对象
Process()
功能:创建进程对象
参数: name: 给创建的进程起一个名字 默认Process-1 Process-2 等等
target:绑定的函数
args: 元组 给target函数按照位置传参
kwargs: 字典 给target函数按照键值传参
p.strat()
功能:启动进程,此时进程被创建。自动运行进程函数。
p.join([timeout])
功能:阻塞等待挥手响应的进程。
参数:超时时间(不传参数的话一直等待)
*multiprocessing 创建进程是原来进程的子进程,创建后父子进程各自执行互不影响。
*子进程同样是复制父进程的空间,子进程对内容的修改不会影响父进程空间。
*join回收子进程,会有效的组织僵尸进程的产生。
*通常使用multiprocessing创建进程,父进程只要作进程的创建和回收,不做其它工作。
import multiprocessing as mp
from time import sleep
#进程事件
def fun():
sleep(3)
print("子进程事件")
#创建进程对象
p = mp.Process(target = fun)
#启动进程
p.start()
sleep(2)
print("这是父进程")
#回收进程
p.join()
print("=========")
#利用multiprocessing创建多个子进程
from multiprocessing import Process
from time import sleep
import os
def th1():
sleep(3)
print("吃饭")
print(os.getppid(),"-----",os.getpid())
def th2():
sleep(2)
print("睡觉")
print(os.getppid(),"-----",os.getpid())
def th3():
sleep(4)
print("打豆豆")
print(os.getppid(),"-----",os.getpid())
things = [th1,th2,th3]
process = []
for th in things:
p = Process(target = th) #每次创建新的进程,P会被覆盖掉。
process.append(p) #保存进程对象
p.start()
for p in process:
p.join()
运行结果:
睡觉
7414 ----- 7416
吃饭
7414 ----- 7415
打豆豆
7414 ----- 7417
multiprocessing对象的属性:
p.is_alive() 判断进程声明周期状态。返回值True False
p.name 进程名称 默认为Process-1 如果起名字则为自定义名称
p.pid 创建的进程的PID
p.daemon
必须在start前设置,如果设置daemon为True就不再使用join
默认值为False 父进程退出不会影响子进程运行。
如果设置为True 则父进程退出子进程也会退出。
from multiprocessing import Process
from time import sleep
def worker(sec,name):
for i in range(3):
sleep(sec)
print("I'm %s"%name)
print("I'm working!")
#通过args给函数传参。
#通过kwargs给函数传参。两个方式可以同时使用。
p = Process(target = worker,args = (2,'Levi'))
p.start()
#判断进程状态
print(p.is_alive())
p.join()
创建自己的进程类:
1、继承Process类
2、重写 __init__ 并且调用父类的__inint__
3、重写run方法,此时生成对象后,调用start就会自动运行run
from multiprocessing import process
import time
class ClockProcess(Process):
def __init__(self,value):
#调用父类的__init__,保证父类的属性也在。
super().__init__()
self.value = value
#重写run方法
def run(self):
for i in range(5):
print("The time is {}".\
format(time.ctime()))
#用自己的类创建进程对象
p = ClockProcess(2)
#调用父类的start,自动执行run
#自动执行run
p.start()
多进程
优点:能并行执行多个任务,提高效率
创建方便,运行独立, 不受其他进程影响。
缺点:进程的创建和删除都需要消耗计算机的资源。
进程池技术:
原因:如果有大量的任务需要多进程完成,且可能现需要频繁的创建和删除进程,给计算机带来大量的资源消耗。
原理:在进程池内运行一定数量进程,通过这些进程完成进程队列中的事件,直到事件执行完毕,减少进程不必要的创建删除过程。
1、实时操作方法:
1、创建进程池,在进程池中放入适当进程。
2、将事件加入到进程池队列。
3、事件不断运行,直到所有的事件运行完毕。
4、关闭进程池,回收进程。
#创建进程池对象
#参数:表示进程池中有多少进程
pool = Pool(processes)
#功能:将事件放入进程池队列。事件放入后,自动启动。
#参数:func 要执行的事件
# args 给func用元组传参
# kwds 给func用字典传参
#返回值:返回事件对象 通过get()方法获取事件函数返回值
pool.appy_async(func,args,kwds)
#功能:关闭进程池,不能在添加新的事件。
pool.close()
#功能:阻塞等待回收进程池,没有参数。
pool.join()
#功能:将要完成的事件放入进程池
#参数:func 要完成的事件函数
# iter 可得迭代对象给func传参
#返回值:事件函数的返回值列表
pool.map(func,iter)
from multiprocessing import Pool
from time import sleep,ctime
#事件函数
def worker(msg):
sleep(2)
print(msg)
#创建进程池,放4个进程
pool = Pool(processes = 4)
result = []
#将事件放入进程池
for i in range(10):
msg = "hello %d"%i
r = pool.apply_async(func = worker,args = (msg,))
result.append(r)#保存进程事件的返回值。
#关闭进程池
pool.close()
#回收进程池
pool.join()
#返回事件函数的返回值
for i in result:
print(i.get())
#使用map方法
from multiprocessing import Pool
from time import sleep
def fun(num):
sleep(2)
print(num)
return num**2
if __name__ == "__main__":
#创建进程池
pl = Pool(4)
#使用map将事件放入进程池。
r = pl.map(fun,range(10))
print(r)
pl.close()
pl.join()
os.path.getsize(path)
功能:获取一个文件的大小
参数:文件