python 并发编程 多进程 互斥锁与join区别
互斥锁与join
互斥锁和join都可以把并发变成串行
以下代码是用join实现串行
from multiprocessing import Process import time import json class Foo(object): def search(self, name): with open("db.txt", "r") as f_read: dic = json.load(f_read) time.sleep(1) # 模拟读数据的网络延迟 print("<%s>用户 查看剩余票数为 [%s]" % (name, dic["count"])) def get(self, name): with open("db.txt", "r") as f_read: dic = json.load(f_read) if dic["count"] > 0: dic["count"] -= 1 time.sleep(1) # 模拟写数据的网络延迟 with open("db.txt", "w") as f_write: json.dump(dic, f_write) print("<%s> 购票成功" % name) print("剩余票数为 [%s]" % dic["count"]) else: print("没票了,抢光了") def task(self, name): self.search(name) self.get(name) if __name__ == "__main__": obj = Foo() for i in range(1,11): # 模拟并发10个客户端抢票 p = Process(target=obj.task, args=("路人%s" % i,)) p.start() p.join()
执行结果
<路人1>用户 查看剩余票数为 [1] <路人1> 购票成功 剩余票数为 [0] <路人2>用户 查看剩余票数为 [0] 没票了,抢光了 <路人3>用户 查看剩余票数为 [0] 没票了,抢光了 <路人4>用户 查看剩余票数为 [0] 没票了,抢光了 <路人5>用户 查看剩余票数为 [0] 没票了,抢光了 <路人6>用户 查看剩余票数为 [0] 没票了,抢光了 <路人7>用户 查看剩余票数为 [0] 没票了,抢光了 <路人8>用户 查看剩余票数为 [0] 没票了,抢光了 <路人9>用户 查看剩余票数为 [0] 没票了,抢光了 <路人10>用户 查看剩余票数为 [0] 没票了,抢光了
发现使用join将并发改成串行,确实能保证数据安全,
但join会把 ,整个程序所有进程都变成串行, 连查看都变成串行了
但问题是连查票操作,也变成只能一个一个人去查了,很明显大家查票时应该是并发地去查询而无需考虑数据准确与否,此时join与互斥锁的区别就显而易见了,
join是将一个任务整体串行,而互斥锁的好处则是可以将一个任务中的某一段代码串行,比如只让task函数中的get任务串行
互斥锁只把要共享数据修改的那段代码变成串行
四 总结
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行地修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1、效率低(共享数据基于文件,而文件是硬盘上的数据)
2、需要自己加锁处理
因此我们最好找寻一种解决方案能够兼顾:
1、效率高(多个进程共享一块内存的数据)
2、帮我们处理好锁问题。
这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,因而队列才是进程间通信的最佳选择。
我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。