并发编程
概要
- 操作系统发展史
- 多道技术
- 实现该技术需包含
- CPU工作机制
- 并发现象
- 进程理论
- 进程与程序的区别
- 单核CPU中,起多个进程怎么办,怎么调度?
- 进程的三状态
- 同步与异步, 阻塞与非阻塞
- 进程其他
- 怎么用代码创建进程
- join方法
- 语法格式
- 作用
- 进程间通信特点: 之间隔离,无法通信
- 进程对象方法
"""学习并发编程其实就是在学习操作系统的发展史(底层逻辑)""" 1.穿孔卡片时代 穿孔卡片即程序 CPU的利用率极低 其中: 制作程序即卡片,输入程序即将卡片输入机器期间CPU都未工作,所以效率低 2.联机批处理系统 将多个程序员的程序一次性录入磁带中 之后交由输入机输入并由CPU执行 3.脱机批处理系统 现代计算机的雏形(远程输入 高速磁带 主机) 其中: 远程输入到的设备相当于硬盘; 高速磁带相当于内存; 主机就是指CPU; 根据木桶效应: CPU处理速度特别快,而硬盘读取速度太慢,所以才有了高速磁带2 多道技术 2.1 实现该技术需包含:
- 服务员: 单核CPU(看清楚,不是多核CPU)
- 服务员的能力:
- 能快速切换到其他程序提供服务
- 离开某服务对象前会记住当时的服务状态,下次再服务时无缝衔接
- 理解
- 做个比喻: CPU是计算机这个公司的核心员工;操作系统是公司CEO
- CPU工作机制
- 当某个程序进入IO状态的时候 操作系统会停止CPU对其服务
- 当某个程序长时间占用CPU的时候 操作系统也会停止CPU对其服务
- 看起来某种事情同时发生的现象
- 看起来同时发生,实际并不是
- 相似词语
- 并行:多个程序同时执行(每个程序都有为其提供服务的服务员)
- 比如: 一个饭店只有一个服务员,但是却能同时服务五桌客人,这种"同时服务五桌客人"的现象就叫并发
- 实现办法:
- 利用某桌客人看菜单,喝茶时间服务其他桌客人.这样每桌客人都感觉被服务;
- 需要服务员的能力
- 1) 跑的快,以至于每桌客人在需要服务的时候服务员都在身边
- 2) 能记住每桌客人之前的服务状态,以便下次无缝衔接,这样每桌客人才不会被感觉敷衍
- 多级队列 + 时间片轮转法
- 多级队列
- 把程序执行次序分层,先运行第一层的程序,再运行第二层的程序,以此循环到最后一层,然后再到第一层,再循环
- 时间片轮转法
- 将1sec分成几份,越是下一层执行的程序被分配的份额越多
- 先来先服务
- 若前边有一个花费时间特久才运行完程序,后边有一个很短时间就可以运行完的程序,对后边的这种短时间就可以运行完的程序不友好
- 短作业优先调度算法
- 若有几千个短时间就能运行完的程序,先运行完他们再运行耗费时间长的程序,对后边这个需要长时间运行的程序不友好
- 就绪态
- 运行态
- 阻塞态
- 新进程
- 就绪态 -> 运行态
- 已经在运行的进程
- 运行态 ->阻塞态 ->就绪态 ->运行态
- 运行态 (时间片到)-> 就绪态 -> 运行态
- " 同步与异步"和"阻塞与非阻塞"组合使用
- 最快组合: 异步非阻塞
- 创建进程: 是在内存中申请一块空间,然后在里边运行程序
- # 执行此代码即告诉操作系统开设一个新的进程 异步提交(原来的主进程,本页面程序仍在运行)
- # 操作系统听到x.start(), 就准备开设一个子进程,需要准备以下才开设,不是马上
from multiprocessing import Process import time import os def test(name): print(os.getpid()) # 获取进程号 print(os.getppid()) # 获取父进程号 print('%s正在运行' % name) time.sleep(3) print('%s已经结束' % name) if __name__ == '__main__': p = Process(target=test, args=('jason',)) #生成一个进程对象;即把test函数申请一块内存空间,执行 p.start() # 告诉操作系统开设一个新的进程,代码能执行到这, 表明有本页面是一个代码正在内存的一块空间运行,即有一个进程在进程; 操作系统马上在内存空间弄一块空间给test程序,原进程还在运行,所以是异步提交 print(os.getpid()) # print('主进程') # 右键"运行"本页代码,就是开了一个进程 """ 在windows中开设进程类似于导入模块 从上往下再次执行代码 一定需要在__main__判断语句内执行开设进程的代码 在linux中是直接将代码完整的复制一份执行 不需要在__main__判断语句内执行 """
方式2 面向对象方式
from multiprocessing import Process import time class MyProcess(Process): # 一定要继承Process,因为 def __init__(self, name): super().__init__() # 执行super()类名对应的init方法 self.name = name # 给类对象加一个name属性 def run(self): print('%s正在运行' % self.name) time.sleep(3) print('%s已经结束' % self.name) if __name__ == '__main__': p = MyProcess('jason') p.start() print('主进程')4.2 xxx.join 方法
- 功能: 主进程等待子进程结束后运行
from multiprocessing import Process import time def test(name, n): print('%s is running' % name) time.sleep(n) print('%s is over' % name) if __name__ == '__main__': p_list = [] start_time = time.time() for i in range(1, 4): p = Process(target=test, args=(i, i)) p.start() p_list.append(p) p.join() #第一个子进程运行完,运行主进程,也就是进入下一for循环,开启第二个子进行,运行完,运行主进程代码,也就是进入下一for循环,开启第三个子循环,运行结束,然后再运行主程序,即接下来代码 print(time.time() - start_time) print('主进程')多个子进程异步(并行)运行
from multiprocessing import Process import time def test(name, n): print('%s is running' % name) time.sleep(n) print('%s is over' % name) if __name__ == '__main__': p_list = [] start_time = time.time() for i in range(1, 4): p = Process(target=test, args=(i, i)) p.start() p_list.append(p) for p in p_list: p.join() print(time.time() - start_time) print('主进程')4.3 进程之间数据相互隔离,无法通信
- "内存中两块空间"即两个进程之间数据是相互隔离的
from multiprocessing import Process money = 100 def test(): global money money = 999 print(money) if __name__ == '__main__': p = Process(target=test) p.start() # 先确保子进程运行完毕了 再打印 p.join() print(money) # 输出结果先999;下一行100 # 结果(想下为啥) # 999 # 1004.4 进程对象方法 4.41 查看进程号 1)查看当前进程号 方式1 方式2 2)查看当前进程号的父进程号 查看进程号 代码
import os from multiprocessing import Process,current_process import time print(os.getpid(), os.getppid()) # 获取当前进程号-方式1 获取当前进程的父进程号 # print(current_process().pid) # 获取当前进程号-方式2 time.sleep(40)演示及cmd中查看进程号对应进程 4.42 其他- 1)查看进程名字, 2)杀死子进程, 3)判断当前进程是否存活
import os from multiprocessing import Process, current_process import time def test(name, n): print('%s is running' % name) time.sleep(n) print('%s is over' % name) if __name__ == '__main__': p = Process(target=test, args=('jason', 3)) p.start() print(p.name) # 查看进程名字 p.terminate() # 杀死子进程 time.sleep(0.1) # 主进程睡0.1才可看出"下行代码"效果;不加本行,因为操作系统反应需要时间,下行结果是True print(p.is_alive()) # 判断当前进程是否存活 print('主')