python 并发编程
并发编程
让你的程序可以同时处理多个任务(多道技术)
什么是进程
-
当程序在被操作系统运行的时候就是一个进程
计算机发展史
1.第一代计算机(1940-1955):真空管和穿孔卡片
2.第二代计算机(1955-1965):晶体管和批处理系统
3.第三代计算机(1965-1980):集成电路芯片和多道程序设计
4.第四代计算机(1980-至今):个人计算机
多道技术
产生背景:想要在单个处理器的情况下实现多个进程并发的效果 实现原理:
-
空间上的复用性
将内存分成多块,一次性读取多个程序
-
时间上的复用性 将进程执行时间进行切片,让处理器利用率提升到最大
缺点:纯计算程序切换会降低效率,但是单核下必须切.
并发与并行
并发: 单个CPU,让进程看起来像同时进行(多道技术)
并行: 多个CPU,多核,进程实际上就是在同时进行
程序与进程的关系
程序就是一堆代码 运行的程序的就是进程
获取进程id
-
PID : 系统会给每个进程分配一个id,相当于人的身份证
os.getpid() # 获取自己的进程id
-
PPID : (parent PID)即父进程的id
os.getppid() # parent 获取父进程id
阻塞和非阻塞:程序的状态(就绪 运行 阻塞)
阻塞: 程序遇到IO操作或者sleep等需要等待的操作,导致后续代码不能被执行
非阻塞: 非阻塞与阻塞相反,表示程序能被CPU正常运行
-
程序运行的三状态
-
运行 : 程序正在运行
-
就绪 :当程序没有在阻塞状态时,就会进入就绪状态等待CPU执行
-
阻塞:程序遇到IO或其他不需要用到CPU的操作时,就会进入阻塞
-
2.python中实现并发
-
python中开启子进程的两种方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 第一种 #--------------------------------------------------------------------------------------- from multiprocessing import Process import os def task(): print('self', os.getpid()) print('parent', os.getppid()) print('task run') # Windows创建子进程时, 子进程会将父进程的代码加载一遍, 导致重复创建子进程 # 所以一定要将创建子进程的代码放在main下面 if __name__ == '__main__': p = Process(target=task) # 创建一个表示进程的对象,但并不是真正的创建进程 p.start() # 给操作系统发送通知,要求操作系统开启进程 # 执行结果 self 12636 parent 11724 task run #--------------------------------------------------------------------------------------- # 第二种 #--------------------------------------------------------------------------------------- # 创建子进程的第二种方式:继承Process 覆盖run方法 在子进程启动后自动执行run方法 # 可以添加其他功能,不影响主进程 class Myprocess(Process): def __init__(self, url): self.url = url super().__init__() def run(self): # 子类方法只能定义为run,其他不能执行,如果要执行其他功能在run中调用 print('下载文件...', self.url) print('run run run !') if __name__ == '__main__': p = Myprocess('www.baidu.com/a.mp4') p.start() # 执行结果 下载文件... www.baidu.com/a.mp4 run run run !
多个进程间的相互隔离
-
即多个进程之间的内存空间是相互隔离的,数据增删查改互不影响
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 1000 def task(): global a # 修改子进程中的全局变量a,由于内存空间隔离,父进程中a任然是1000 a = 0 print(id(a)) print('子进程的a', a) if __name__ == '__main__': print(id(a)) p = Process(target=task) p.start() print('父进程中的a',a) # 执行结果(父子进程中的a值和id都不同,说明空间相互隔离) 4697808 父进程中的a 1000 491482560 子进程的a 0
join()
-
join() : 提升绑定程序的优先级
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from multiprocessing import Process import time def task(i): print('我是%s号进程' % i) time.sleep(2) if __name__ == '__main__': start_time = time.time() ps = [] for i in range(5): p = Process(target=task, args=(i, )) p.start() # 开启5个子进程,执行过程无阻塞(子进程sleep是子进程的操作) ps.append(p) for p in ps: p.join() # 子进程运行结束才会运行父进程,提升了子进程的优先级 print(time.time()-start_time) print('over') # 执行结果 我是1号进程 我是2号进程 我是0号进程 我是3号进程 我是4号进程 2.3151323795318604 over # 执行结果分析 # 1.执行顺序混乱:由于子进程之间的优先级相同,操作系统随机执行 # 2.结束总时间为什么是2秒多:由于for循环开启子进程无阻塞,所以时间极短,然后由于join绑定后子进程优先级高于父进程所以要等子进程运行完毕才执行父进程,所以总时间就是2秒加上for循环开启子进程时间.
进程的其他内置方法
print(p.is_alive()) # 判断进程是否活着 print(p.terminate()) # 终止这个进程 p.daemon() 守护进程,守护的进程终止,守护进程也终止
孤儿进程与僵尸进程
孤儿进程: 没有parent的进程称为孤儿进程,parent进程终止后,子进程就变成孤儿进程了,无害 没有父进程,系统会自动回收
僵尸进程: 子进程执行任务完毕后,但是还残留一些信息(id, 进程名),但是父进程没有处理这些 残留信息导致残留信息占用系统内存,有父进程,系统不会回收(有害) 处理方法:终止父进程,使僵尸进程变成孤儿进程,被系统回收