多道技术/进程初识
多道技术
1.空间上的复用
- 多个程序共用一套计算机硬件
2.时间上的复用
- 切换+保存状态
- 1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
- 2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)
- 并发:看起来像同时运行的就可以
- 并行:真正意义上的同时执行
- 单核的计算机能不能实现并行,但是可以实现并发
进程的同步与异步
程序:一坨代码
进程:正在运行的程序
同步异步:表示的是任务的提交方式
- 同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)
- 异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的 但是是用过其他方式获取)
阻塞非阻塞:表示的程序的运行状态
- 阻塞:阻塞态
- 非阻塞:就绪态 运行态
强调:同步异步 阻塞非阻塞是两对概念 不能混为一谈
创建进程的两种方式
windows创建进程会将代码以模块的方式,从上往下执行一遍
linux会直接将代码完完整整的拷贝一份,fork一份
第一种方式:
-
windows创建进程一定要在__main__ 代码块内创建,否则报错
from muliprocessing import Process import time def test(name): print('%s is running' %name) time.sleep(3) print('%s is over' %name) # windows创建进程一定要在if __name__ == '__main__':代码块内创建,否则报错 if __name__ == '__main__': p = Process(target=test,args=('egon',)) # 创建一个进程对象 p.start() # 告诉操作系统,帮你创建一个进程 print('主') # 主 # egon is running # egon is over
总结:
- 创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去;而且一个进程对应在内存就是一块独立的内存空间
- 进程与进程之间数据是隔离的,无法直接交互,但是可以通过某些技术实现间接交互
第二种方式:
from muliprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s is running' %self.name) time.sleep(3) print('%s is over' %self.name) if __name__ == '__main__': p = MyProcess(target=test,args=('egon',)) p.start() print('主') # 主 # egon is running # egon is over
join方法
from muliprocessing import Process import time def test(name,i): print('%s is running' %name) time.sleep(i) print('%s is over' %name) if __name__ == '__main__': p_list = [] start_time = time.time() for i in range(3): p = Process(target=test,args=('进程%s'%i,i)) # 主进程代码等待字进程运行结束才继续运行 p.start() # 仅仅是告诉操作系统帮你创建一个进程,至于先后顺序则是随机的 p_list.append(p) for p in p_list: p.join() # 主进程代码等待子进程运行结束 print('主') print(time.time() - start_time) # 进程0 is running # 进程0 is over # 主 # 所有步骤花费的时间显示
进程间数据是隔离的
from muliprocessing import Process import time money = 100 # 主进程内的money def test(): global money money = 888888 # 子进程内的money if __name__ == '__main__': p = Process(target=test) p.start() p.join() print(money) # 100
进程对象及其他方法
在操作系统cmd终端中可以通过tasklist + pid码来查询
from muliprocessing import Process,current_process import os import time def test(name): print('%s is running' %name,current_process().pid) print('%s is running' %name,'子进程%s' %os.getpid(),'父进程%s' %os.getppid()) time.sleep(30) print('%s is over' %name) if __name__ == '__main__': p = Process(target=test,args=('egon',)) p.start() p.terminate() # 杀死当前进程,其实是告诉操作系统帮你杀死一个进程 time.sleep(0.1) print(p.is_alive()) # 判断进程是否存活 返回True:马上杀死,False:已杀死 print('主',current_process().pid) print('主',os.getpid(),'主主进程:%s' %os.getppid()) # 主 23040 主主进程:20352 # egon is running 21991 # egon is over
僵尸进程
父进程回收子进程资源的两种方式
- 1.join方法
- 2.父进程正常死亡
PS:所有的进程都会步入僵尸进程
孤儿进程
- 子进程没死,父进程意外死亡
- 针对linux,会有一个儿童福利院(init),如果父进程意外死亡,他所创建的子进程都会被福利院收养
守护进程
from muliprocessing 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() print('皇帝jason寿终正寝') # 皇帝jason寿终正寝
互斥锁
定义:当多个进程操作同一份数据时,会造成数据的错乱,这个时候必须加锁处理,将并发变成串行,虽然降低了效率,但是提高了数据的安全
注意:
- 1.锁不要轻易使用,容易造成死锁现象
- 2.只在处理数据的部分加锁,不要在全局加锁
设计一个模拟抢票软件
from muliprocessing 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' %(1,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: # 票数减1 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=test,args=('egon',)) p.start()