并发编程
概要
-
操作系统发展史
-
多道技术
-
实现该技术需包含
-
CPU工作机制
-
并发现象
-
进程理论
-
进程与程序的区别
-
单核CPU中,起多个进程怎么办,怎么调度?
-
进程的三状态
-
同步与异步, 阻塞与非阻塞
-
进程其他
-
怎么用代码创建进程
-
join方法
-
语法格式
-
作用
-
进程间通信特点: 之间隔离,无法通信
-
进程对象方法
详细
1 操作系统发展史
"""学习并发编程其实就是在学习操作系统的发展史(底层逻辑)""" 1.穿孔卡片时代 穿孔卡片即程序 CPU的利用率极低 其中: 制作程序即卡片,输入程序即将卡片输入机器期间CPU都未工作,所以效率低 2.联机批处理系统 将多个程序员的程序一次性录入磁带中 之后交由输入机输入并由CPU执行 3.脱机批处理系统 现代计算机的雏形(远程输入 高速磁带 主机) 其中: 远程输入到的设备相当于硬盘; 高速磁带相当于内存; 主机就是指CPU; 根据木桶效应: CPU处理速度特别快,而硬盘读取速度太慢,所以才有了高速磁带
2 多道技术
2.1 实现该技术需包含:
-
服务员: 单核CPU(看清楚,不是多核CPU)
-
服务员的能力:
-
能快速切换到其他程序提供服务
-
离开某服务对象前会记住当时的服务状态,下次再服务时无缝衔接
2.2 补充- CPU工作机制
-
理解
-
做个比喻: CPU是计算机这个公司的核心员工;操作系统是公司CEO
-
CPU工作机制
-
当某个程序进入IO状态的时候 操作系统会停止CPU对其服务
-
当某个程序长时间占用CPU的时候 操作系统也会停止CPU对其服务
2.3 并发现象
2.31定义
-
看起来某种事情同时发生的现象
-
看起来同时发生,实际并不是
-
相似词语
-
并行:多个程序同时执行(每个程序都有为其提供服务的服务员)
2.32 实际案例比喻
-
比如: 一个饭店只有一个服务员,但是却能同时服务五桌客人,这种"同时服务五桌客人"的现象就叫并发
-
实现办法:
-
利用某桌客人看菜单,喝茶时间服务其他桌客人.这样每桌客人都感觉被服务;
-
需要服务员的能力
-
1) 跑的快,以至于每桌客人在需要服务的时候服务员都在身边
-
2) 能记住每桌客人之前的服务状态,以便下次无缝衔接,这样每桌客人才不会被感觉敷衍
3 进程理论
3.1 进程与程序的区别
程序: 存储在硬盘上的有一定功能的代码(死的)
进程: 正在运行的程序(活的)
3.2 单核CPU中,起多个进程怎么办,怎么调度的?
最优方法
-
多级队列 + 时间片轮转法
-
多级队列
-
把程序执行次序分层,先运行第一层的程序,再运行第二层的程序,以此循环到最后一层,然后再到第一层,再循环
-
时间片轮转法
-
将1sec分成几份,越是下一层执行的程序被分配的份额越多
其他方法
-
先来先服务
-
若前边有一个花费时间特久才运行完程序,后边有一个很短时间就可以运行完的程序,对后边的这种短时间就可以运行完的程序不友好
-
短作业优先调度算法
-
若有几千个短时间就能运行完的程序,先运行完他们再运行耗费时间长的程序,对后边这个需要长时间运行的程序不友好
3.3 进程的三状态(程序运行的三种状态***)
1) 三状态
-
就绪态
-
运行态
-
阻塞态
2) 三状态转换图
-
新进程
-
就绪态 -> 运行态
-
已经在运行的进程
-
运行态 ->阻塞态 ->就绪态 ->运行态
-
运行态 (时间片到)-> 就绪态 -> 运行态
![](https://img2020.cnblogs.com/blog/2657057/202201/2657057-20220113233818420-19088703.png)
3) 理解
阻塞态: 操作系统恨不得CPU每秒工作,所以发生那么凡是需要让cpu等待的操作都是阻碍其工作的,如 用户输入;
![](https://img2020.cnblogs.com/blog/2657057/202201/2657057-20220113233818447-583773001.png)
3.4 同步与异步, 阻塞与非阻塞
-
" 同步与异步"和"阻塞与非阻塞"组合使用
-
最快组合: 异步非阻塞
3.41 同步与异步(******)
'''用于描述任务的提交方式'''
同步
提交一个请求后,期间不做任何事,原地等待结果返回
异步
提交一个请求后,不原地等待任务的返回结果, 直接去做其他事 结果由反馈机制自动提醒
3.42 阻塞与非阻塞(******)
'''用于描述任务的执行状态'''
阻塞:阻塞态
非阻塞:就绪态 运行态
4 进程其他
4.1 创建进程-代码方式
-
创建进程: 是在内存中申请一块空间,然后在里边运行程序
两种方式
方式1
xxx.start()
-
# 执行此代码即告诉操作系统开设一个新的进程 异步提交(原来的主进程,本页面程序仍在运行)
-
# 操作系统听到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 # 100
4.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中查看进程号对应进程
![](https://img2020.cnblogs.com/blog/2657057/202201/2657057-20220113233818433-860716658.png)
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('主')