多进程
什么是守护进程?
守护进程就是一个进程(b)守护者另外一个进程(a),当进程a结束后进程b跟着同时结束
应用场景?
主进程认为自己的事情一旦结束之后就没有必要使用子进程的情况下使用守护进程
import timefrom multiprocessing import Process
def task(): print('妃子的一生') time.sleep(2) print('妃子凉了') if name = main: fz = Process(target= task) fz.daemon = True fz.start() print('皇帝登基了') time.sleep(2) print('当了十年皇帝') print('皇帝驾崩了')
2.互斥锁
互斥锁就是互相排斥的锁
锁的本质就是一个bool类型的数据,在执行代码前会先判断这个值
在使用锁的时候必须保证锁是同一个锁
当多个进程共享一个数据时,可能会造成数据错乱
1.使用join来让这些进程串行.这样会导致无法并发并且在执行任务的顺序固定
2.使用锁,将需要共享的数据进行加锁,其他进程在访问数据时就必须等待当前进程使用完毕
多个任务共享一个数据的时候:串行会降低效率,但是不会出现问题;并发效率高,但是数据
可能会出现错乱
lock.acquire() 是一个阻塞的函数 会等到别的进程释放锁才能继续执行
lock.release() 解锁
Lock 与Rlock的区别
Rlock 表示可重入锁. 特点是:可以执行多次acquire
在不执行多次acquire时和普通lock 没有任何区别
如果在多进程中使用Rlock 并且一个进程a 执行了多次acquire,其他进程b要想获得这个锁 需
要进程a 把锁解开 并且锁了几次就要解几次普通锁如果多次执行acquire将会锁死
1 import time 2 def task(i,lock): 3 lock.acquire() 4 lock.acquire() 5 print(i) 6 time.sleep(3) 7 lock.release() 8 lock.release() 9 #第一个过来 睡一秒 第二个过来了 睡一秒 第一个打印1 第二个打印2 10 11 if __name__ == '__main__': 12 lock = RLock() 13 p1 = Process(target=task,args=(1,lock)) 14 p1.start() 15 16 p2 = Process(target=task, args=(2,lock)) 17 p2.start()
join 与锁的区别
1.join中的顺序是固定的
2.join是完全串行,而锁可以实现部分代码串行,其他代码还是并发
互斥锁的使用场景
import json, time, random from multiprocessing import Process, Lock file_path = r'E:\新建文件夹\ticket.json' def check_ticket(user): time.sleep(random.randint(1, 2)) with open(file_path, 'r', encoding='utf-8') as f: dic = json.load(f) print('%s查看剩余票%s' % (user, dic['count'])) def get_ticket(user): time.sleep(random.randint(1, 2)) with open(file_path, 'rt', encoding='utf-8') as f: dic = json.load(f) if dic['count'] > 0: time.sleep(random.randint(1, 2)) dic['count'] -= 1 with open(file_path, 'wt', encoding='utf-8') as f1: json.dump(dic, f1) print('%s 购票成功' % user) def task(user, lock): check_ticket(user) lock.acquire() get_ticket(user) lock.release() if __name__ == '__main__': lock = Lock() for i in range(10): p = Process(target=task, args=(i, lock)) p.start()
死锁
死锁是指锁无法打开了,导致程序卡死
def task2(l1,l2,i): l2.acquire() print("筷子被%s抢走了" % i) l1.acquire() print("盘子被%s抢走了" % i) print("吃饭..") l1.release() l2.release() if __name__ == '__main__': l1 = Lock() l2 = Lock() Process(target=task1,args=(l1,l2,1)).start() Process(target=task2,args=(l1,l2,2)).start()
IPC 进程间的通信
由于进城之间内存是相互独立的,所以需要对应积极的方案,能够使的进程间可以相互传递数据
1.使用共享文件,多个进程同时读写一个文件 特点是:IO速度慢,传输数据大小不受限制
2.管道:是基于内存的,速度快,但是是单向的用起来麻烦
3.申请共享内存空间,多个进程可以共享这个内存区域 特点:速度快,但是数据量不能太大
from multiprocessing import Manager,Process,Lock def work(d): # with lock: d['count']-=1 if __name__ == '__main__': with Manager() as m: dic=m.dict({'count':100}) #创建一个共享的字典 p_l=[] for i in range(100): p=Process(target=work,args=(dic,)) p_l.append(p) p.start() for p in p_l: p.join() print(dic)
队列
队列不仅仅用于进程间的通讯,也是一种常见的数据容器
特点:先进先出
优点:可以保证数据不会错乱,即使在多线程下,因为其put和get 默认都是阻塞的
from multiprocessing import Queue # q = Queue(1) # 创建一个队列 最多可以存一个数据 # q.put("张三") # print(q.get()) # q.put("李四") # put默认会阻塞 当容器中已经装满了 # print(q.get()) # print(q.get()) # get默认会阻塞 当容器中已经没有数据了 # print("over") q = Queue(1) # 创建一个队列 最多可以存一个数据 q.put("张三") # q.put("李四",False) # 第二个参数 设置为False表示不会阻塞 无论容器是满了 都会强行塞 如果满了就抛异常 print(q.get()) print(q.get(timeout=3)) # timeout 仅用于阻塞时 # q.put("李四") # put默认会阻塞 当容器中已经装满了 # # print(q.get()) # print(q.get()) # get默认会阻塞 当容器中已经没有数据了 # # print("over")
生产者和消费者模型
生产者:产生数据的一方 消费者:处理数据的一方
import random from multiprocessing import Process,Queue import time # 爬数据 def get_data(q): for num in range(5): print("正在爬取第%s个数据" % num) time.sleep(random.randint(1,2)) print("第%s个数据 爬取完成" % num) # 把数据装到队列中 q.put("第%s个数据" % num) def parse_data(q): for num in range(5): # 取出数据 data = q.get() print("正在解析%s" % data) time.sleep(random.randint(1, 2)) print("%s 解析完成" % data) if __name__ == '__main__': # 共享数据容器 q = Queue(5) #生产者进程 produce = Process(target=get_data,args=(q,)) produce.start() #消费者进程 customer = Process(target=parse_data,args=(q,)) customer.start()