一、什么是进程
程序:一行行代码组成
进程:正在运行的程序
进程即正在执行的一个过程。进程是对正在运行程序的一个抽象
二、多道技术
1.空间上的复用
多个程序共用一套计算机硬件
2.时间上的复用
切换+保存状态
1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)
并发:看起来像同时运行的就可以
并行:真正意义上的同时执行
单核的计算机能不能实现并行,但是可以实现并发
三、同步异步、阻塞非阻塞
1.进程调度:
时间片轮转法+多级反馈队列
2.进程三状态图
同步异步:表示的是任务的提交方式
阻塞非阻塞:表示程序的运行状态
ps:同步异步 阻塞非阻塞是两对概念 不能混为一谈
四、进程在python中的运用
1.创建进程的两种方式
使用Process实例化
继承Process类重写run方法
from multiprocessing import Process import time def test(name): print('%s is running'%name) time.sleep(2) print('%s is over'%name) if __name__ == '__main__': # window创建进程一定要在if __name__ == '__main__':代码块内创建,否则会报错 p = Process(target=test,args=('Tom',)) # 创建一个进程对象 p.start() # 告诉操作系统帮你创建一个进程 print('主')
# 创建进程的第二张方式 from multiprocessing 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(2) print('%s is over' % self.name) if __name__ == '__main__': p = MyProcess('tom') p.start() print('主')
特点:
1.在windos操作系统中,创建进程会将代码以模块的方式 从上往下执行一遍
linux会直接将代码完完整整的拷贝一份
2.创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去
一个进程对应在内存就是一块独立的内存空间
3.进程与进程之间数据时隔离的,无法直接交互
但是可以通过某些技术实现间接交互
2.进程join方法:
通过join方法实现主进程等待子进程运行结束后,自己才继续运行
from multiprocessing 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 = [] # 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() p = Process(target=test,args=('egon',1)) p1 = Process(target=test,args=('kevin',2)) p2 = Process(target=test,args=('jason',3)) start_time = time.time() p.start() # 仅仅是告诉操作系统帮你创建一个进程 至于这个进程什么时候创 操作系统随机决定 p1.start() p2.start() p2.join() p.join() p1.join() # 主进程代码等待子进程运行结束 才继续运行 # p.join() # 主进程代码等待子进程运行结束 print('主') print(time.time() - start_time)
3.进程间对象及其他方法
p.terminate() # 杀死进程,告诉操作系统杀死一个进程
p.is_alive() # 判断进程是否存活
os.getpid() 和 current_process().pid 一样 # 获取进程ID
os.getppid() # 获取父进程ID
from multiprocessing 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(3) 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()) # 判断进程是否存活 # print('主',current_process().pid) print('主',os.getpid(),'主主进程:%s'%os.getppid())
4.进程间数据互相隔离
from multiprocessing import Process import time money = 100 def test(): global money money = 99999999 if __name__ == '__main__': p = Process(target=test) p.start() p.join() print(money)
5.守护进程
会随着主进程的结束而结束。
daemon 这一句必须在start之前使用
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
p.daemon = True # 将该进程设置为守护进程 这一句话必须放在start语句之前 否则报错
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寿正终寝')
6.互斥锁 ****
mutex = Lock() # 生成一把锁
mutex.acquire() # 抢锁
mutex.release() # 释放锁
当多个进程操作同一份数据的时候 会造成数据的错乱,这个时候必须加锁处理,
将并发变成串行
虽然降低了效率但是提高了数据的安全
注意:
1.锁不要轻易使用,容易造成死锁现象
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()
7.僵尸进程与孤儿进程
父进程回收子进程资源的两种方式
1.join方法
2.父进程正常死亡
所有的进程都会步入僵尸进程
孤儿进程
子进程没死 父进程意外死亡
针对linux会有儿童福利院(init) 如果父进程意外死亡他所创建的子进程都会被福利院收养
五、进程间的通信
1.实现多进程间数据传递,通过Queue方法,Queue是多进程安全的队列
from multiprocessing import Queue q = Queue(5) # 括号内可以传参数,表示的是这个队列的最大存储数 # q.put() 往队列中添加数据 q.put(1) # q.full() # 判断队列是否满了 q.put(2) q.put(3) q.put(4) q.put(5) # print(q.full()) # 返回True,False # q.put(6) # 当队列满了之后,子放入数据,不会报错,会原地等待,直到队列中有数据被取走(阻塞态) q.get() # 取数据,当队列中数据被取完之后,再次获取,程序会阻塞,直到有人往队列中放入值。 q.empty() # 判断队列中的数据是否取完 q.get_nowait() # 取值,没有值不等待直接报错 ''' full get_nowait empty 都不适用于多进程的情况 '''
2.通信ipc机制
from multiprocessing import Process,Queue def producer(q): q.put('baby') def consumer(q): print(q.get()) if __name__ == '__main__': q = Queue() p = Process(target=producer,args=(q,)) c = Process(target=consumer,args=(q,)) p.start() c.start() ''' 子进程放数据,主进程获取数据 两个子进程互相放取数据 '''
3.生产者消费模型
如何做到消费者消费完数据之后代码立刻结束
1.利用join等待生产者生产完数据 再往队列中添加特定信息(None)
有几个消费者就必须有几个None
2.JoinableQueue 能够被join的q
q.task_done() 告诉队列数据被取出
q.join() 等待队列数据完全被取完
将所有的消费者设置为守护进程
from multiprocessing import Process, Queue, JoinableQueue import time import random def producer(name,food,q): for i in range(10): data = '%s做了%s%s'%(name,food,i) time.sleep(random.random()) q.put(data) print(data) def consumer(name,q): while True: data = q.get() if data == None:break print('%s吃了%s'%(name,data)) time.sleep(random.random()) q.task_done() # 告诉队列已经从中取出了一个数据,并且处理完毕了 if __name__ == '__main__': q = JoinableQueue() # 能够被等待的数列,与q.task_done连用,表示已经处理好了,拿出了数据 p = Process(target=producer, args=('tom','馒头',q)) p1 = Process(target=producer, args=('jason', '馒头', q)) c = Process(target=consumer, args=('jack',q)) c1 = Process(target=consumer, args=('jerry', q)) p.start() p1.start() c.daemon = True c1.daemon = True # 守护,子进程随主进程结束而关闭, c.start() c1.start() p.join() # 等待子进程结束自己再运行 p1.join() q.join() # 等队列中数据全部取出 ''' JoinableQueue内部有自己机制,检测数据确确实实被拿完 q.task_done q.join ''' # q.put(None) # 可以实现,太low # q.put(None)