Python 计算机发展史 多道技术 进程 守护进程 孤儿和僵尸进程 互斥锁
一 计算机发展史
操作系统本质上是一个软件
主要功能:
1、控制硬件,隐藏丑陋复杂的硬件细节
2、将无序的硬件竞争变得有序
二 多道技术(第三代计算机出现)
1.产生背景:针对单核,实现并发 ps: 现在的主机一般是多核,那么每个核都会利用多道技术 有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个 cpu中的任意一个,具体由操作系统调度算法决定。 2.空间上的复用:如内存中同时有多道程序 3.时间上的复用:复用一个cpu的时间片 强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样 才能保证下次切换回来时,能基于上次切走的位置继续运行
空间复用:多个程序共用一套计算机硬件,每个程序间的内存都是相互隔离的(物理隔离)
时间复用(切换+保存):当一个程序执行IO操作时,切换到另一个程序来执行,光切换还不行 必须在切换前保存当前的状态 以便与恢复执行。
注意:并不是多道就一定提高了效率
如果多个任务都是纯计算 那么切换反而降低了效率
遇到IO操作才应该切换 这才能提高效率
三 同步 异步 阻塞 非阻塞 并发 并行
#同步和异步指的是任务的提交方式
1.同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)
2.异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的 但是是用过其他方式获取)
#阻塞和非阻塞指的程序运行的状态
1.阻塞:当程序执行过程中遇到了IO操作,在执行IO操作时,程序无法继续执行其他代码,称为阻塞!
2.非阻塞:
强调:同步异步 阻塞非阻塞是两对概念 不能混为一谈
四 进程
1.什么是进程:正在运行的程序,程序是程序员编程的一堆代码(字符串),当这对代码被系统加载的内存中执行时,就有了进程(也是操作系统在调度和进行资源分配的基本单位)
当一个进程a开启了另一个进程b时,a称为b的父进程,b称为a的子进程
2.实现进程的两种方式
1.实例化Process类 from multiprocessing import Process import time def task(name): print('%s is running' %name) time.sleep(3) print('%s is done' %name) if __name__ == '__main__': # 在windows系统之上,开启子进程的操作一定要放到这下面 # Process(target=task,kwargs={'name':'egon'}) p=Process(target=task,args=('jack',)) # 实例化产生进程对象 p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态 print('======主') 2.继承Process类 并覆盖init 和 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() 需要注意的是 1.在windows下 开启子进程必须放到`__main__`下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程 2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管 print('主')
3.进程中的
Process的对象具备一个join函数
用于提高子进程优先级 ,使得父进程等待子进程结束
from multiprocessing import Process def task(): print("子进程 Game Over") if __name__ == '__main__': p = Process(target=task) p.start() p.join() # 等待子进程执行完毕,将子进程的优先级提高 print("主进程 Game Over") # 子进程 Game Over # 主进程 Game Over
4.进程中的内存都是相互隔离的
from multiprocessing import Process x = 10 def task(): global x x = 100 print("子进程",x) # 子进程 100 if __name__ == '__main__': p = Process(target=task) p.start() print("父进程",x) # 父进程 10 创建进程就是在内存中重新开辟一块内存空间 将允许产生的代码丢进去 一个进程对应在内存就是一块独立的内存空间 进程与进程之间数据是隔离的 无法直接交互 但是可以通过某些技术实现间接交互
5.进程对象及其他方法
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('主')
6.pid和ppid
#1.什么是pid: 一个操作系统中通常都会运行多个应用程序,也就是多个进程,那么如何来区分进程呢?系统会给每一个进程分配一个进程编号即PID,如同人需要一个身份证号来区分。
#2.
# 在python中可以使用os模块来获取ppid import os print("self",os.getpid()) # 当前进程自己的pid print("parent",os.getppid()) # 当前进程的父进程的pid
四 进程中的孤儿进程和僵尸进程
1.什么是孤儿进程:
孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程
例如:qq聊天中别人发给你一个链接,点击后打开了浏览器,那qq就是浏览器的父进程,然后退出qq,此时浏览器就成了孤儿进程
孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。
2.什么是僵尸进程:僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!
五 进程中的守护进程
1.什么是守护进程:一个进程可以守护另一个进程。开启守护进程是为了并发执行某个任务,但这个任务如果伴随着主进程的结束而没有存在的意义,就可以将子进程设为守护进程。比如:qq接收到一个视频文件,于是开启了一个子进程来下载,如果中途退出了,下载任务就没必要继续下去。如果b是a的守护进程,a是被守护的进程,如果a挂了,则b也随之结束。
from multiprocessing import Process import time def test(name): print('%s总管正常活着'%name) time.sleep(3) print('%s总管正常死亡'%name) if __name__ == '__main__': p = Process(target=test,args=('egon',)) p.daemon = True # 将该进程设置为守护进程 这一句话必须放在start语句之前 否则报错 p.start() time.sleep(0.1) print('皇帝jason寿正终寝')
互相排斥的锁,我在这站着你就别过来,(如果这个资源已经被锁了,其他进程就无法使用了)
需要强调的是: 锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行
为什么需要互斥锁:
解决方案1:
加join, 弊端 1.把原本并发的任务变成了穿行,避免了数据错乱问题,但是效率降低了,这样就没必要开子进程了 2.原本多个进程之间是公平竞争,join执行的顺序就定死了,这是不合理的
解决方案2:
就是给公共资源加锁,互斥锁 互斥锁 互相排斥的锁,我在这站着你就别过来,(如果这个资源已经被锁了,其他进程就无法使用了)
锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行
锁和join的区别:
1.join是固定了执行顺序,会造成父进程等待子进程 锁依然是公平竞争谁先抢到谁先执行,父进程可以做其他事情
2.最主要的区别: join是把进程的任务全部串行 锁可以锁任意代码 一行也可以 可以自己调整粒度
# 当多个进程操作同一份数据的时候 会造成数据的错乱 # 这个时候必须加锁处理 # 将并发变成串行 # 虽然降低了效率但是提高了数据的安全 # 注意: # 1.锁不要轻易使用 容易造成死锁现象 # 2.只在处理数据的部分加锁 不要在全局加锁 # # 锁必须在主进程中产生 交给子进程去使用 注意1: 不要对同一把执行多出acquire 会锁死导致程序无法执行 一次acquire必须对应一次release ```python l = Lock() l.acquire() print("抢到了!") l.release() l.acquire() print("强哥毛线!") ``` 注意2:想要保住数据安全,必须保住所有进程使用同一把锁 from multiprocessing import Process,Lock import time import json # 查票 def search(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) print('用户%s查询余票为:%s'%(i,t_d.get('ticket'))) # 买票 def buy(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) time.sleep(1) if t_d.get('ticket') > 0: # 票数减一 t_d['ticket'] -= 1 # 更新票数 with open('data','w',encoding='utf-8') as f: json.dump(t_d,f) print('用户%s抢票成功'%i) else: print('没票了') def run(i,mutex): search(i) mutex.acquire() # 抢锁 只要有人抢到了锁 其他人必须等待该人释放锁 buy(i) mutex.release() # 释放锁 if __name__ == '__main__': mutex = Lock() # 生成了一把锁 for i in range(10): p = Process(target=run,args=(i,mutex)) p.start()