进程同步(multiprocess.Lock、multiprocess.Semaphore、multiprocess.Event)
锁 —— multiprocess.Lock
通过刚刚的学习,我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题。
当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 #虽然可以用文件共享数据实现进程间通信,但问题是: #1.效率低(共享数据基于文件,而文件是硬盘上的数据) #2.需要自己加锁处理 #因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。 #队列和管道都是将数据存放于内存中 #队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来, #我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
信号量 —— multiprocess.Semaphore(了解)
#互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。 #假设商场里有4个迷你唱吧,所以同时可以进去4个人,如果来了第五个人就要在外面等待,等到有人出来才能再进去玩。 #实现: #信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和#V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。 #信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念
# 信号量介绍Semaphore # 多进程中的组件 # ktv # 4个 # 一套资源 同一时间 只能被n个人访问 # 某一段代码 同一时间 只能被n个进程执行 import time#引入时间模块 import random#引入随机数 from multiprocessing import Process#引入进程模块 from multiprocessing import Semaphore#引入信号模块 # sem = Semaphore(4)#实例化4个信号 # sem.acquire() # print('拿到第一把钥匙') # sem.acquire() # print('拿到第二把钥匙') # sem.acquire() # print('拿到第三把钥匙') # sem.acquire() # print('拿到第四把钥匙') # sem.acquire() # print('拿到第五把钥匙') def ktv(i,sem): sem.acquire() #获取钥匙 print('%s走进ktv'%i) #进入ktv time.sleep(random.randint(1,5))#随机选择1到5之间的数 print('%s走出ktv'%i)#打印走出ktv sem.release() #还钥匙 if __name__ == '__main__' :#如果为真 sem = Semaphore(4)#实例化一个红绿灯 for i in range(20):#循环20个数 p = Process(target=ktv,args=(i,sem))#开启一个进程对象 p.start()#开启这个进程
事件 —— multiprocess.Event(了解)
#python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。 #事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 #event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。 #clear:将“Flag”设置为False #set:将“Flag”设置为True
#红绿灯示例 # 通过一个信号 来控制 多个进程 同时 执行或者阻塞 # 事件 # from multiprocessing import Event # 一个信号可以使所有的进程都进入阻塞状态 # 也可以控制所有的进程解除阻塞 # 一个事件被创建之后,默认是阻塞状态 # e = Event() # 创建了一个事件 # print(e.is_set()) # 查看一个事件的状态,默认被设置成阻塞 # e.set() # 将这个事件的状态改为True # print(e.is_set()) # e.wait() # 是依据e.is_set()的值来决定是否阻塞的 # print(123456) # e.clear() # 将这个事件的状态改为False # print(e.is_set()) # e.wait() # 等待 事件的信号被变成True # print('*'*10) # set 和 clear # 分别用来修改一个事件的状态 True或者False # is_set 用来查看一个事件的状态 # wait 是依据事件的状态来决定自己是否在wait处阻塞 # False阻塞 True不阻塞 # 红绿灯事件 import time#引入时间模块 import random#引入随机模块 from multiprocessing import Event,Process#引入进程模块和时间模块 def cars(e,i):#定义一个函数 if not e.is_set():#如果信号灯为真的时候 print('car%i在等待'%i)#打印内容 e.wait() # 阻塞 直到得到一个 事件状态变成 True 的信号 print('