目录python
早期程序员使用穿孔卡片编程,linux
只有一个计算机机房,一次只能读取一个卡片,程序员
因此形成CPU的利用率极低编程
后来出现了联机批处理系统,支持多用户去使用一个计算机机房并发
再后来出现了脱机批处理系统,有了高速磁盘,提升了文件的读取速度,优势是提升了CPU的利用率异步
基于单核状况下研究分为单道和多道,单道即多个程序在使用CPU时是串行运行函数
当代计算机使用的技术 是多道技术性能
一个CPU能够提供多个用户去使用优化
切换 + 保存状态操作系统
若CPU 遇到 IO 操做,会当即将当前执行程序CPU使用权断开
优势:CPU利用率高
若一个程序使用CPU的时间过长,会当即将当前执行程序CPU使用权断开
缺点:程序执行效率低
并行:指的是看起来像同时在运行,实际上是多个程序不停 切换 + 保存状态
并发:真正意义上的同时运行,在多核(多个CPU)状况下,同时执行多个程序
执行中的程序叫作进程(Process),是一个动态的概念
在linux中查看进程信息:top
61546 root 20 0 15028 1196 836 R 4.4 0.1 0:00.35 top
程序:一堆代码文件
进程:一堆代码文件运行的过程
当代操做系统调度:
时间片轮转法 + 分级反馈队列
一、 先来先服务调度
a,b 程序,若a程序先来,先占用CPU
缺点:程序a先使用,程序b必须等待程序a使用CPU完毕以后才能使用
二、 短做业优先调度
a,b程序,谁的用时短,优先调度谁使用CPU
缺点:
若程序a使用时间最长,有N个程序使用时间段,
必须等待全部用时短的程序结束后才能使用
三、 时间片轮转法
CPU执行的时间1秒钟,若加载N个程序,将时间等分红多n个时间片供程序执行
四、 分级反馈队列
将执行优先级分多层级别
1级:优先级最高
2级:优先级第二,以此类推
。。。
全部程序建立时都会进入就绪态,准备调度
调度后的进程,进入运行态
凡是遇到IO操做的进程,都会进入阻塞态
若IO结束,必须从新进入就绪态
指的是提交任务的方式
同步:如有两个任务须要提交,在提交第一个任务时,必须等待该任务执行结束后,才能继续提交并执行第二个任务。
同步演示:
# coding=utf-8 import time def test(): # 睡一秒 也是IO操做 time.sleep(1) # 计算操做不属于IO操做 n = 0 for i in range(1000): n += i print(n) if __name__ == '__main__': test() # 先执行完test函数,再执行下面的代码,同步操做 print("hello world")
异步:
如有两个任务须要提交,在提交第一个任务时,不须要原地等待,当即能够提交并执行第二个任务。
阻塞:
阻塞态,遇到IO 必定会阻塞
非阻塞:
就绪态
运行态
''' 建立进程方式一: ''' from multiprocessing import Process import time def task(name): print(f"{name} 子任务start...") time.sleep(2) print(f"{name} 子任务end....") if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.start() print("主进程...")
代码解释:
p = Process(target=task,args=("qinyj",)):实例化一个对象p,Process类参数:target=函数名,args=函数参数(必须是元组 + ,)
p.start():向操做系统发送指令开启一个子进程,至于这个子进程何时启动,要看机器的硬件性能
打印结果:
主进程... qinyj 子任务start... qinyj 子任务end....
''' 建立进程方式二: ''' from multiprocessing import Process import time class MyProcess(Process): def run(self): print(f"子任务 start...") time.sleep(2) print(f"子任务 end....") if __name__ == '__main__': p = MyProcess() p.start() print("主进程...")
代码解释
p = MyProcess():实例化本身写的一个类,这个类必须继承Process
p.start():向操做系统发送指令开启一个子进程,至于这个子进程何时启动,要看机器的硬件性能
打印结果:
主进程... 子任务 start... 子任务 end....
那么为何咱们要用if name == 'main':?,
是由于Windows在执行这个文件的时候,会自动import导入这个文件,导入这个文件至关于又从新执行了一遍里面的代码,这样子进程就从新又被建立了出来,子进程而后又运行到建立子进程的代码。。。无限循环,最后报错。那么在Linux中执行这个文件不会自动import导入,它所建立的子类代码仅仅是执行的函数代码,fork了一块新的内存空间执行这个函数,因此不会报错
那么咱们为了统一使用,最好加上if name == 'main':,意思是做为执行文件执行的时候,判断它的名字,条件符合了再执行下面的代码,这样不管哪一个平台运行代码都不会报错。
# coding=utf-8 from multiprocessing import Process import time def task(name): print(f"{name} 子任务 start...") time.sleep(2) print(f"{name} 子任务 end...") if __name__ == '__main__': p1 = Process(target=task,args=("qinyj",)) p2 = Process(target=task,args=("jack",)) p3 = Process(target=task,args=("qyj",)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print("主进程...")
p.join:用来告诉操做系统,让子进程结束后,父进程在结束
# coding=utf-8 from multiprocessing import Process x = 100 def func(): print("函数执行") global x x = 200 print(f"函数内部的x:{x}") if __name__ == '__main__': p = Process(target=func) p.start() p.join() print(f"主进程的x:{x}") 函数执行 函数内部的x:200 主进程的x:100
# coding=utf-8 from multiprocessing import Process from multiprocessing import current_process import os import time def task(name): print(f"{name} 子任务start...",f"子进程进程号:{current_process().pid}") print(f"{name} 子任务start...",f"子进程进程号:{os.getpid()}",f"父进程进程号{os.getppid()}") time.sleep(200) print(f"{name} 子任务end.....",current_process().pid) if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.start() time.sleep(1) print(p.is_alive()) p.terminate() time.sleep(1) print(p.is_alive()) p.join() print("主程序start..",os.getpid()) print("主主进程start...",os.getppid()) qinyj 子任务start... 子进程进程号:9132 qinyj 子任务start... 子进程进程号:9132 父进程进程号7348 True False 主程序start.. 7348 主主进程start... 2812
代码解释:
p.terminate():直接告诉操做系统,终止子进程
print(p.is_alive()):打印子进程的存活状态(True or False)
current_process().pid:获取当前的pid号
os.getpid():获取当前的pid号
os.getppid():获取当前父进程的pid号
cmd中查看进程号:tasklist |findstr 进程号
C:\Users\Administrator>tasklist | findstr 6724 # 查看子进程pid号即python解释器的进程 python.exe 6724 Console 1 30,576 K C:\Users\Administrator>tasklist | findstr 2812 # 查看主进程pid号即pycharm解释器的进程 pycharm64.exe 2812 Console 1 909,528 K
由进程产生的pid号会自动 由主进程回收,若是子进程存在,主进程死掉了,那么子进程就是僵尸进程
两种进程号回收的方法条件:
一、 join:主进程能够等待子进程结束 pid一块儿被回收
二、 主进程正常结束,守护进程开启,子进程与主进程 pid被回收
# coding=utf-8 from multiprocessing import Process from multiprocessing import current_process import os import time def task(name): print(f"{name} 子进程start...",current_process().pid) time.sleep(5) print(f"{name} 子进程end...",current_process().pid) if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.daemon = True p.start() time.sleep(1) # 停留1s 让子进程启动 print("主进程启动...") qinyj 子进程start... 5680 主进程启动...
代码解释:
p.daemon = True:添加守护进程,True
守护进程表示的是 主进程死的时候,不管子进程在干什么,直接干掉子进程,跟着主程序一块儿死掉
僵尸进程指的是子进程已经结束,但pid号还存在没有被销毁,能够比喻人死了,身份证尚未注销,那这个pid就会一直被占用,那么操做系统的进程号是有限的,若是产生大量的僵尸进程,将由于没有可用的进程号而致使系统不能产生新的进程,此为僵尸进程的危害,应当避免
那么咱们能够经过查找这个pid号kill 手动杀掉回收
僵尸进程的缺点:占用pid号,占用操做系统的资源
孤儿进程指的是子进程还在执行,但父进程意外结束,可是有操做系统优化机制,会提供一个孤儿院,专门帮那些已经死了的主进程 回收那些没有父亲的子进程,在linux操做系统中,这个孤儿院就是init进程,pid号是1
1 root 20 0 19364 644 424 S 0.0 0.1 0:17.45 init