目录
抢票小程序
version1(并发)
#db.txt{"count": 1}--------------------------------from multiprocessing import Processimport json,time,osdef search(): time.sleep(1) # 模拟网络io with open('db.txt',mode='rt',encoding='utf-8') as f: res = json.load(f) print(f'还剩{res["count"]}')def get(): with open('db.txt',mode='rt',encoding='utf-8') as f: res = json.load(f) # print(f'还剩{res["count"]}') time.sleep(1) # 模拟网络io if res['count'] > 0: res['count'] -= 1 with open('db.txt',mode='wt',encoding='utf-8') as f: json.dump(res,f) print(f'进程{os.getpid()} 抢票成功') time.sleep(1.5) # 模拟网络io else: print('票已经售空啦!!!!!!!!!!!')def task(): search() get()if __name__ == '__main__': for i in range(15): p = Process(target=task) p.start()---------------------------------------------并发运行效率高,但竞争写同一个文件,数据写入错乱,只有一张票,却成功卖给多个人
于是进行加锁处理
version2(加锁处理)
#db.txt{"count": 1}--------------------------------from multiprocessing import Process,Lockimport json,time,osdef search(): time.sleep(1) # 模拟网络io with open('db.txt',mode='rt',encoding='utf-8') as f: res = json.load(f) print(f'还剩{res["count"]}')def get(): with open('db.txt',mode='rt',encoding='utf-8') as f: res = json.load(f) # print(f'还剩{res["count"]}') time.sleep(1) # 模拟网络io if res['count'] > 0: res['count'] -= 1 with open('db.txt',mode='wt',encoding='utf-8') as f: json.dump(res,f) print(f'进程{os.getpid()} 抢票成功') time.sleep(1.5) # 模拟网络io else: print('票已经售空啦!!!!!!!!!!!')def task(lock): search() # 锁住 lock.acquire() get() lock.release() # 释放锁头if __name__ == '__main__': lock = Lock() # 写在主进程是为了让子进程拿到同一把锁. for i in range(15): p = Process(target=task,args=(lock,)) p.start()
思考:
购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全,那为什么不用join将并发改成 串行?答:使用join将并发改成串行,确实可以保证数据的安全,但就变成查票操作也就变成了只能一个一个人去查了,很明显大家查票是并发的去查询而无需数据准确与否,此时join和互斥锁的区别就显而易见了,join是将一个任务 整体串行,而互斥锁的好处就是可以将一个任务的某个代码串行,比如只让task函数中的get任务串行
总结:
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。虽然可以用文件共享数据实现进程间通信,但问题是:1.效率低(共享数据基于文件,而文件是硬盘上的数据)2.需要自己加锁处理#因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。1 队列和管道都是将数据存放于内存中2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。