并发编程-多进程
多道技术
实现原理:
1.空间复用
统一时间,加载多个任务到内存中,多个进程之间内存区域需要相互隔离,这种隔离是物理层面的隔离,其目的是为了保证数安全
2.时间复用
指的是,操作系统会在多个进程之间做切换执行
切换任务的两种情况
1.当一个进程遇到了IO操作 时会自动切换
2.当一个任务执行时间超过阈值会强制切换
注意:在切换前必须保存状态,以便后续恢复执行
并且 频繁的切换其实也需要消耗资源
当所有任务都没有IO操作时,切换执行效率反而降低,但是为了保证并发执行 必须牺牲效率
简单的总结就是切换加保存
有了多道技术,计算机就可以同时并发的处理多个任务
串行
自上而下运行
并行
真正的同时运行,必须具备多核CPU ,有几个核心就能并行几个任务,当任务数量超过核心数还是并发执行
并发
并发指的是多个任务同时被执行,并发编程指的是编写支持多任务并发的应用程序。
进程
进程指的是正在运行的程序,是一系列过程的统称,也是操作系统在调度和进行资源分配的基本单位
进程是实现并发的一种方式
一个程序可以产生多个进程
PID和PPID
PID:系统给进程的编号
PPID:当一个进程a开启了另一个进程b,那么a被称为b的父进程。当pycharm运行了py文件,那么pycharm就是py文件的父进程
并行与并发,阻塞与非阻塞
并发指的是,多个事件同时发生了,需要一直切换完成
并行指的是,多个事件同时进行,互不影响
阻塞状态是因为程序遇到了IO操作,或是sleep,导致后续的代码不能被CPU执行
非阻塞与之相反,表示程序正在被CPU执行。
进程有三种状态,就绪态,运行态,阻塞态
python中实现多进程
python中开启子进程的两种方式
方式1:实例化Process类
from multiprocessing import Process
import time
def task(name):
print(f'{name} is running ')
time.sleep(3)
print('f{name} is fone')
if __name__=='__main__':
#在windows系统之上,开启子进程的操作一定要放在这个里面
#Process(target=task,kwargs={'name':egon})
p=Process(target=task,args=('jack',))#参数放在元祖里
p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态
print('主')
方式2:继承Process,并覆盖run方法
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super(MyProcess,self).__init__()
self.name=name
def run(self):
print('%s is running' %self.name)
time.sleep(3)
print('%s is done' %self.name)
if __name__ == '__main__':
p=MyProcess('jack')
p.start()
print('主')
需要注意的是
1.在windows下 开启子进程必须放到__main__
下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程
2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管
3.start仅仅是给操作系统发送消息,而操作系统创建进程是要花费时间的,所以会有两种情况发送
3.1开启进程速度慢于程序执行速度,先打印”主“ 在打印task中的消息
3.2开启进程速度快于程序执行速度,先打印task中的消息,在打印”主“
进程间内存相互隔离
在一个进程内修改了全局变量,其他进程不受影响。就像是运行两个文件,一个修改了,另外一个不受影响。
from multiprocessing import Process
import time
x=1000
def task():
global x
x=0
print('儿子死啦',x)
if __name__ == '__main_
print(x)
p=Process(target=task)
p.start()
time.sleep(5)
print(x)
###
1000
儿子死啦 0
1000
join函数:
可以让一个进程等待另一个进程结束后再运行
from multiprocessing import Process
import time
x=1000
def task():
time.sleep(3)
global x
x=0
print('儿子死啦',x)
if __name__ == '__main__':
p=Process(target=task)
p.start()
p.join() # 让父亲在原地等,如果不加join函数,则会先执行print(x)
print(x)
from multiprocessing import Process
import time,random
x=1000
def task(n):
print('%s is runing' %n)
time.sleep(n)
if __name__ == '__main__':
start_time=time.time()
p1=Process(target=task,args=(1,))
p2=Process(target=task,args=(2,))
p3=Process(target=task,args=(3,))
p1.start()
p2.start()
p3.start()
p3.join() #3s
p1.join()
p2.join()
print('主',(time.time() - start_time))
start_time=time.time()
p_l=[]
for i in range(1,4):
p=Process(target=task,args=(i,))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print('主',(time.time() - start_time))
Process对象常用属性
from multiprocessing import Process
def task(n):
print('%s is runing' %n)
time.sleep(n)
if __name__ == '__main__':
start_time=time.time()
p1=Process(target=task,args=(1,),name='任务1')
p1.start() # 启动进程
print(p1.pid) # 获取进程pid
print(p1.name) # 获取进程名字
p1.terminate() # 终止进程
p1.join() # 提高优先级
print(p1.is_alive()) # 获取进程的存活状态
print('主')
孤儿进程和僵尸进程
孤儿进程:
指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程。
孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。
僵尸进程:
指的是当父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程会成为一个僵尸进程,这种情况只在linux下出现。因为windows中进程完全是独立的没有任何关联。
如果父进程先退出,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源,
python已经帮我们自动处理了僵尸进程的回收工作。