Python 之并发编程之进程下(事件(Event())、队列(Queue)、生产者与消费者模型、JoinableQueue)
八:事件(Event())
# 阻塞事件:
e = Event() 生成事件对象e
e.wait() 动态给程序加阻塞,程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False]
# 如果是True 不加阻塞
# 如果是False 加阻塞
# 控制这个属性的值
# set() 方法 将这个属性的值改成True
# clear() 方法 将这个属性的值改成False
#is_set() 方法 判断当前属性是否为True (默认上来是False)
#(1)各方法基本使用
e = Event()
# 获取该对象中成员属性是True 还是False
print(e.is_set())
print(1)
# 用e.set() 把该对象的成员值变成True
e.set() # True
print(e.is_set())
e.wait()
print(2)
# 用e.clear() 把该对象的成员值变成False
e.clear()
print(3)
print(e.is_set())
e.wait()
print(4)
输出结果为:
False
1
True
2
3
False
4 不运行,4之前是False,是加阻塞,所有不运行。
#(2)模拟红绿灯效果
#例:
from multiprocessing import Process, Event
import time, random # traffic_light(e):
def traffic_light(e):
print("红绿灯")
while True: if e.is_set():
#print(e.is_set())
# 当前绿灯等待1秒
time.sleep(1)
# 切换成红灯
print("红灯亮")
e.clear()
#print(e.is_set())
else:
# 为当前红灯等待1秒
time.sleep(1)
# 等完1秒之后,变成绿灯
print("绿灯亮")
e.set() # 小车在遇到红灯时停,绿灯时行
def car(e, i):
if not e.is_set():
print("car %s 在等待" % (i))
# wait获取的值是False ,所有加阻塞
e.wait()
print("car %s 通行了" % (i)) """
# 车跑完了,但是红绿灯仍然执行
if __name__ == "__main__":
e = Event()
# 启动交通灯
p1 = Process(target=traffic_light, args=(e,))
p1.start() for i in range(10):
time.sleep(random.randrange(0, 2))
p2 = Process(target=car, args=(e, i))
p2.start() """ # ###优化版
# 车跑完了,红绿灯这个进程也结束掉
if __name__ == "__main__":
e = Event()
lst = [] #启动交通灯
p1 = Process(target=traffic_light, args=(e,))
p1.daemon = True
p1.start() for i in range(20):
time.sleep(random.randrange(0,2))
p2 = Process(target=car, args=(e,i))
p2.start()
lst.append(p2) # 循环等待每一辆车通过红绿灯
for i in lst:
i.join() print("程序彻底执行结束")
执行结果为:
红绿灯
car 0 在等待
绿灯亮
car 0 通行了
car 1 通行了
car 2 通行了
car 3 通行了
红灯亮
绿灯亮
car 4 通行了
红灯亮
car 5 在等待
绿灯亮
car 5 通行了
car 6 通行了
红灯亮
car 7 在等待
car 8 在等待
绿灯亮
car 7 通行了
car 8 通行了
car 10 通行了
car 9 通行了
car 12 通行了
car 11 通行了
红灯亮
car 13 在等待
绿灯亮
car 13 通行了
car 14 通行了
car 15 通行了
car 16 通行了
car 17 通行了
红灯亮
car 18 在等待
car 19 在等待
绿灯亮
car 19 通行了
car 18 通行了
程序彻底执行结束
运行结果解析:
Car每次通过的数量是不一定的,因为car子进程的产生是根据随机睡眠时间而定的。
如果把注释部分打开,将优化版注释,则是程序将一直执行,车走完了,红绿灯依然那边红绿打印。
九:队列Queue
#先进先出 原则
#(1)基本语法
from multiprocessing import Process,Queue
q = Queue()
# 1.把数据放q队列中 put
q.put(111)
# 2.把数据从队列里面拿出来 get
res = q.get()
print(res)
# 3.当队列里面的值都拿出来,已经没有数据的时候,在获取会阻塞
res = q.get() #直接阻塞
# 4.get_nowait() 无论有没有都拿,如果拿不到,直接报错
'''
get_nowait 内部要依赖queue模块来实现
没有完全优化好,不推荐使用,想用就用put和get分别设置和获取,就可以了
# res = q.get_nowait() 不推荐使用
# print(res)
# try .. except .. 捕捉异常
try:
print(q.get_nowait())
# 特指queue.Empty 这种错误,执行某些逻辑.
except queue.Empty:
print("empty")
except:
print("其他")
'''
# (2)可以使用queue 指定队列的长度
#最多放3个,超过最大长度,就执行阻塞
#例:
from multiprocessing import Process,Queue
q = Queue(3)
q.put(1)
q.put(2)
q.put(3)
# print(q.get())
#q.put(4) #阻塞的情况
# q.put_nowait(4) #超过队列最大长度,在存值直接报错 (不推荐使用) # (了解 不常用 full empty)
# 队列值满了返回真,不满返回假
res = q.full()
print(res)
# 判断队列中是否为空
res = q.empty()
print(res)
输出结果为:
True
False
# (3) 进程之间的通讯
def func(q):
# 1.主进程添加的值,子进程可以通过队列拿到
res = q.get()
print("我是子进程", res)
q.put("a2222") if __name__ == "__main__":
q1 = Queue()
p = Process(target=func, args=(q1,))
p.start() #q1.get() #如果添加这个会阻塞,因为取不到值
# 主进程添加数据
q1.put("a111")
p.join()
# 2.子进程添加的值,主进程通过队列可以拿到
print("主进程:", q1.get())
结果输出:
我是子进程 a111
主进程: a2222
运行结果分析:
输出的主进程a222说明,数据子进程中被改变了,所有进程之间的数据可以改变的。
十. 生产者与消费者模型
(1)为什么要使用生产者消费者模型
生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
(2)什么是生产者和消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题,生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的
举例思路:
# 爬虫例子:
1号进程负责获取网页中所有内容
2号进程负责匹配提取网页中的关键字
1号进程就可以看成一个生产者
2号进程就可以看成一个消费者
有时可能生产者比消费者快,反之也是一样
所以生产者和消费者为了弥补之间速度的差异化,加了一个中间的缓冲队列.
生产者和消费者模型从程序上看就是一个存放和拿取的过程.
最为理想的生产者消费者模型是两者之间的运行速度相对同步.
#例:
from multiprocessing import Process, Queue
import random, time # 消费者模型[负责取值]
def consumer(q, name):
while True:
food = q.get()
if food is None:
break
time.sleep(random.uniform(0.1, 1))
print("1.%s吃了一个%s" % (name, food))
# 生产者模型 [负责存之值]
def producer(q,name,food):
for i in range(5):
time.sleep(random.uniform(0.1,1))
print("2. %s 生产了 %s %s" % (name, food, i))
q.put(food+str(i)) if __name__ == "__main__":
q = Queue()
# 创建消费者进程对象
c1 = Process(target=consumer, args=(q,"one"))
# 如果设置守护进程,主进程直接当前消费者模型结束,不能够保证所有数据消费完毕,是一个弊端,而且不理想
c1.start() # 2号消费者
c2 = Process(target=consumer, args=(q,"two"))
c2.start() # 创建生产进程对象
p1 = Process(target=producer, args=(q,"producer_one","糖"))
p1.start() p2 = Process(target=producer, args=(q,"producer_two","巧克力"))
p2.start() p1.join()
p2.join()
q.put(None)
q.put(None)
运行后的结果如下:
2. producer_one 生产了 糖 0
2. producer_two 生产了 巧克力 0
1.one吃了一个巧克力0
1.two吃了一个糖0
2. producer_one 生产了 糖 1
2. producer_two 生产了 巧克力 1
1.one吃了一个糖1
1.two吃了一个巧克力1
2. producer_one 生产了 糖 2
2. producer_two 生产了 巧克力 2
1.one吃了一个糖2
2. producer_one 生产了 糖 3
1.two吃了一个巧克力2
2. producer_two 生产了 巧克力 3
1.one吃了一个糖3
2. producer_two 生产了 巧克力 4
2. producer_one 生产了 糖 4
1.two吃了一个巧克力3
1.one吃了一个巧克力4
1.two吃了一个糖4
结果分析:生产的糖或巧克力一定或被消费完,等消费完主进程才会结束
十一:JoinableQueue
1. 概述
JoinableQueue 与Queue一样也是multiprocessing模块中的一个类,也可以用于创建进程队列。
JoinableQueue 创建可连接的共享进程队列,队列允许队列的消费者通知生产者,队列数据已被成功处理完成。通知过程是使用共享的信号和条件变量来实现的。
2.常用方法与属性
(1)put 存入
(2)get 获取
JoinableQueue除了与Queue相同的方法之外,还具有2个特有的方法
(3)q.task_done()
消费者使用此方法发出信号,表示q.get()返回的项目已经被处理完成。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
每次get一次数据,就执行一次task_done(),可以让中间变量的值减1
(4)q.join()
判断如果队列里面还有值,默认是要加阻塞的。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。
task_done 与 join 通过一个中间变量统计列表元素个数
每放入一个值,队列元素个数加1
通过task_done让当前队列的元素数量减1
最后join查找统计队列的这个变量,如果是0,才不会添加阻塞,放行
#(1)基本用法
from multiprocessing import Process,JoinableQueue
jq = JoinableQueue()
jq.put(11)
print(jq.get())
jq.task_done() #如果没有加这一行代码,后面的代码不执行
jq.join()
print("finish")
#打印结果:
11
finish
下面对task_done 和 join进行举例理解:
#例:
# 优化生产者消费者模型
# 消费者模型 [负责取值]
def consumer(q,name):
while True:
food = q.get()
time.sleep(random.uniform(0.1,1)) #随机生成一个0.1到之间的小数
print("1. %s 吃了一个%s " %(name,food))
q.task_done() # 生产者模型 [负责存值]
def producer(q,name,food):
for i in range(5):
time.sleep(random.uniform(0.1,1))
print("2. %s 生产了 %s %s" % (name,food,i))
q.put(food+str(i)) if __name__ == "__main__":
jq = JoinableQueue()
# 创建消费者
c1 = Process(target=consumer,args=(jq,"one"))
c1.daemon = True
c1.start() # 创建生产者
p1 = Process(target=producer, args=(jq,"two","meat"))
p1.start() p1.join()
# 必须等待队列里面的锁头数据都清空,判断值为0才放行
jq.join()
print("全部结束")
#打印结果为:
2. two 生产了 meat 0
2. two 生产了 meat 1
1. one 吃了一个meat0
1. one 吃了一个meat1
2. two 生产了 meat 2
1. one 吃了一个meat2
2. two 生产了 meat 3
1. one 吃了一个meat3
2. two 生产了 meat 4
1. one 吃了一个meat4
全部结束
结果分析:生产了才能进行消费,join添加了阻塞,只有消费完成程序才运行结束,而c1加了守护进程
Python 之并发编程之进程下(事件(Event())、队列(Queue)、生产者与消费者模型、JoinableQueue)的更多相关文章
- 人生苦短之我用Python篇(队列、生产者和消费者模型)
队列: queue.Queue(maxsize=0) #先入先出 queue.LifoQueue(maxsize=0) #last in fisrt out queue.PriorityQueue( ...
- python之并发编程(线程\进程\协程)
一.进程和线程 1.进程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是 ...
- python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)
9.11 进程池与线程池 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务 池子内什么时候装进程:并发的任务属于计算密集型 池子内什么时候装线程:并发的任务属于I ...
- Python 之并发编程之进程上(基本概念、并行并发、cpu调度、阻塞 )
一: 进程的概念:(Process) 进程就是正在运行的程序,它是操作系统中,资源分配的最小单位. 资源分配:分配的是cpu和内存等物理资源 进程号是进程的唯一标识 同一个程序执行两次之后是两个进程 ...
- Python 之并发编程之进程中(守护进程(daemon)、锁(Lock)、Semaphore(信号量))
五:守护进程 正常情况下,主进程默认等待子进程调用结束之后再结束守护进程在主进程所有代码执行完毕之后,自动终止kill -9 进程号 杀死进程.守护进程的语法:进程对象.daemon = True设置 ...
- Python 之并发编程之线程下
七.线程局部变量 多线程之间使用threading.local 对象用来存储数据,而其他线程不可见 实现多线程之间的数据隔离 本质上就是不同的线程使用这个对象时,为其创建一个只属于当前线程的字典 拿空 ...
- Python并发编程之进程通信
''' 进程间的通信 ''' """ multiprocessing模块支持进程间通信的两种主要形式:管道和队列 都是基于消息传递实现的, ""&qu ...
- python并发编程之进程、线程、协程的调度原理(六)
进程.线程和协程的调度和运行原理总结. 系列文章 python并发编程之threading线程(一) python并发编程之multiprocessing进程(二) python并发编程之asynci ...
- Python 3 并发编程多进程之守护进程
Python 3 并发编程多进程之守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemo ...
随机推荐
- 试题编号: 201903-3 试题名称: 损坏的RAID5
这题的数据未免也太水了,题目的意思好像默认是每块磁盘装载数据的长度是相等的.我写了判断每次取数据是否会超过每块磁盘存的数据的长度,然而并没有什么卵用.交上去20分,写了个数据测了下,如果要求的块太大的 ...
- TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q
TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...
- 吴裕雄 python 机器学习——数据预处理嵌入式特征选择
import numpy as np import matplotlib.pyplot as plt from sklearn.svm import LinearSVC from sklearn.li ...
- Codeforces Round #597 (Div. 2)D(最小生成树)
/*每个点自己建立一座发电站相当于向超级源点连一条长度为c[i]的边,连电线即为(k[i]+k[j])*两点间曼哈顿距离,跑最小生成树(prim适用于稠密图,kruscal适用于稀疏图)*/ #def ...
- 【jQuery基础】
" 目录 #. 介绍 1. 优势 2. 版本 3. jQuery对象 #. 查找标签 1. 选择器 /. 基本选择器 /. 层级选择器 /. 基本筛选器 /. 使用jQuery实现弹框 / ...
- 15 FFT及其框图实现
FFT及其框图实现 \(FFT\)的全称为快速傅里叶变换,但是\(FFT\)并不是一种变换,而是实现\(DFT\)的一种快速算法.当\(N\)比较大时,使用\(FFT\)可大大减少进行\(DFT\)变 ...
- 创建Ajax兼容
var request = new XMLHttpRequest(); IE7以下: var request = new ActiveXObject("Microsoft.XMLHTTP&q ...
- NLP直播-1 词向量与ELMo模型
翻车2次,试水2次,今天在B站终于成功直播了. 人气11万. 主要讲了语言模型.词向量的训练.ELMo模型(深度.双向的LSTM模型) 预训练与词向量 词向量的常见训练方法 深度学习与层次表示 LST ...
- 十、Spring中常用注解-分层整理
1.@Controller: 标注展示层组件(Bean),但是目前该功能与 @Component 相同,用来创建处理http请求的对象 Spring4之后加入的注解,原来在@Controller中 ...
- html5 postMessage 消息传递问题
<script type="text/javascript"> $.fn.extend({ addEvent: function (type, handle, bool ...