上节复习、进程同步控制(Lock/锁、Semaphore/信号量、Event/事件)、进程间通信(队列) 第三十二天 2018.11.21
上节复习:
多进程代码:
from multiprocessing import Process
方法
进程对象.start() 开启一个子进程
进程对象.join() 感知一个子进程的结束
进程对象.terminate() 结束一个子进程
进程对象.is_alive() 查看某个子进程是否还在运行
属性
进程对象.name 进程名
进程对象.pid 进程号
进程对象.daemon 值为True的时候,表示新的子进程是一个守护进程
守护进程 随着主进程代码的执行结束而结束
一定在start之前设置
from multiprocessing import Lock
l = Lock()
l.acquire() # 拿钥匙
会造成数据不安全的操作
l.release() # 还钥匙
多进程的补充---->子进程使用input报错问题
# 报错---->子进程在另一个进程中开启,无法再主进程中干预另一个进程中的子进程
# 运行时input在主进程屏幕输入数据,无法传递到运行中的子进程
from multiprocessing import Process
def func():
num = input('>>>') # 此时运行input时在主进程内输入,开启的子进程无法接收到在主进程中输入的
print(num)
if __name__ == '__main__':
Process(target=func).start()
进程同步控制---->Semaphore(信号量):
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。
semaphore:是一个内置的计数器---->每当调用acquire()时,内置计数器-1;每当调用release()时,内置计数器+1
计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
多进程中的组件:
一套资源 同一时间 只能被n个人访问
某一段代码 同一时间 只能被n个进程执行

import time
import random
from multiprocessing import Process
from multiprocessing import Semaphore
sem = Semaphore(4) # 只有4个acquire
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)
time.sleep(random.randint(1,5))
print('%s走出ktv'%i)
sem.release()
if __name__ == '__main__' :
sem = Semaphore(4)
for i in range(20):
p = Process(target=ktv,args=(i,sem))
p.start()
信号量---->Semaphore---->利用锁---->内部加了一个计数器
信号量和锁的区别---->锁每次只能同时进一个;信号量每次可以同时进入的个数自己控制
进程同步控制---->Event(事件):
通过一个信号 来控制 多个进程 同时 执行或者阻塞事件
通常与函数结合使用,函数不会在事件发生前被执行!
Event事件
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:
分别用来修改一个事件的状态set(建立)----> True或者clear(清除)---->False
is_set 用来查看一个事件的状态
wait 是依据事件的状态来决定自己是否在wait处阻塞
False阻塞 True不阻塞
红绿灯事件:(因为只有True和False两种状态,所以简易的红绿灯事件只使用2种灯)
# 红绿灯事件
import time # 导入time模块,模拟红灯绿灯时间,和车辆到来时间
import random # 导入random模块,模拟车辆到来时间的不规则
from multiprocessing import Event,Process # 导入事件和进程
def cars(e,i): # 只负责按照事件值执行,不修改事件值
if not e.is_set(): # 如果not False---->True(红灯亮了)---->与下面保持一致
print('\033[31mcar%i在等待\033[0m'%i) # 控制台输出格式
e.wait() # 阻塞 直到得到一个 事件状态变成 True 的信号
print('\033[32mcar%i通过\033[0m' % i)
def light(e): # 同时执行
while True: # 红绿灯不会停止无限循环---->可以设置为守护进程强行停止
if e.is_set(): # 查看事件的状态,默认为False阻塞
e.clear() # 将这个事件改为False
print('\033[31m红灯亮了\033[0m') #事件为真时间才执行红灯
else: # 第一次传入为False
e.set() # 将这个事件改为True
print('\033[32m绿灯亮了\033[0m')
time.sleep(2)
if __name__ == '__main__': # 主进程
e = Event() # 创建一个事件对象
traffic = Process(target=light,args=(e,)) # 红绿灯的进程 传入的参数为事件,一个红绿灯不需要编号
traffic.start() # 开启红绿灯进程的子进程---->只有一个红绿灯,只需要一个子进程
for i in range(20): # 模拟来20辆车
car = Process(target=cars, args=(e,i)) # 来车的进程 传入的参数为事件,以及来的车为那个进程的
car.start() # 开启一个来车的子进程---->会被for循环20次
time.sleep(random.random()) # 模拟每两辆来车之间的时间间隔
进程间通信---->Queue(队列):
队列:先进先出
from multiprocessing import Queue,Process
Queue([maxsize])---->创建共享的进程队列。maxsize是队列中允许的最大项数。
q.put(item [, block [,timeout ] ] ) ---->将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。
如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)
timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。
q.get( [ block [ ,timeout ] ] )---->取队列中一个东西,与put方法类似
'''
队列put放入东西,get取出东西
from multiprocessing import Queue
q = Queue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.full()) # 队列是否满了
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.empty())
用while取值
while True:
try:
q.get_nowait() # 没收到取值会报错
except:
print('队列已空')
time.sleep(0.5)
用for取值
for i in range(6):
q.put(i)
'''
from multiprocessing import Queue,Process
def produce(q):
q.put('hello')
def consume(q):
print(q.get())
if __name__ == '__main__':
q = Queue()
p = Process(target=produce,args=(q,))
p.start()
c = Process(target=consume, args=(q,))
c.start()
'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''
from multiprocessing import Queue
q=Queue(3)
#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
# q.put(3) # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
# 如果队列中的数据一直不被取走,程序就会永远停在这里。
try:
q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
print('队列已经满了')
# 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
print(q.full()) #满了
print(q.get())
print(q.get())
print(q.get())
# print(q.get()) # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
try:
q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
print('队列已经空了')
print(q.empty()) #空了
单看队列用法
队列引入生产者、消费者模型:
生产者消费者模型:
# 生产者 进程
# 消费者 进程
import time
import random
from multiprocessing import Process,Queue
def consumer(q,name):
while True:
food = q.get() # 在队列里取出东西,每次取出都为队列的第一个元素
if food is None: # 如果队列里取出的东西为None则停止循环结束子进程
print('%s获取到了一个空'%name)
break
print('\033[31m%s消费了%s\033[0m' % (name,food)) # 谁消费了f
time.sleep(random.randint(1,3))
def producer(name,food,q):
for i in range(10):
time.sleep(random.randint(1,3))
f = '%s生产了%s%s'%(name,food,i) # 谁生产了什么东西 生产的是第几个
print(f)
q.put(f) # 把生产的对象放入队列里
if __name__ == '__main__':
q = Queue(20) # 最多存放20个
# 每个生产者和消费者都是相互独立的,但是整体合起来需要受到Queue(20)控制
p1 = Process(target=producer,args=('Egon','包子',q)) # 两个生产者最多生产20个
p2 = Process(target=producer, args=('wusir','大饼', q))
c1 = Process(target=consumer, args=(q,'alex')) # 两个消费者最多消费20个
c2 = Process(target=consumer, args=(q,'jinboss'))
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
# 两个消费者进程取同一队列,不一定两者取得数量一致
q.put(None) # 当生产者生产完成后,将2个消费者对应的2个结束的None放入队列最后
q.put(None)
生产者消费者模型完善:
import time
import random
from multiprocessing import Process,JoinableQueue
def consumer(q,name):
while True:
food = q.get() # 在队列里取出东西,每次取出都为队列的第一个元素
print('\033[31m%s消费了%s\033[0m' % (name,food)) # 谁消费了f
time.sleep(random.randint(1,3))
q.task_done() # count - 1 向q.join()发送一次信号,证明一个数据已经被取走了
def producer(name,food,q):
for i in range(4):
time.sleep(random.randint(1,3))
f = '%s生产了%s%s'%(name,food,i) # 谁生产了什么东西 生产的是第几个
print(f)
q.put(f) # 把生产的对象放入队列里
q.join() # 阻塞 直到一个队列中的所有数据 全部被处理完毕
if __name__ == '__main__':
q = JoinableQueue(20) # 最多存放20个
# 每个生产者和消费者都是相互独立的,但是整体合起来需要受到Queue(20)控制
p1 = Process(target=producer,args=('Egon','包子',q)) # 两个生产者最多生产20个
p2 = Process(target=producer, args=('wusir','大饼', q))
c1 = Process(target=consumer, args=(q,'alex')) # 两个消费者最多消费20个
c2 = Process(target=consumer, args=(q,'jinboss'))
p1.start()
p2.start()
c1.daemon = True # 设置为守护进程 主进程中的代码执行完毕之后,子进程自动结束
c2.daemon = True
c1.start()
c2.start()
p1.join()
p2.join() # 感知一个进程的结束
在消费者这一端:
每次获取一个数据
处理一个数据
发送一个记号 : 标志一个数据被处理成功
在生产者这一端:
每一次生产一个数据
且每一次生产的数据都放在队列中
在队列中刻上一个记号
当生产者全部生产完毕之后
join信号 : 已经停止生产数据了
且要等待之前被刻上的记号都被消费完
当数据都被处理完时,join阻塞结束
浙公网安备 33010602011771号