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 ...
随机推荐
- 关于原生,webapp,hybird(混合)
链接:https://www.jianshu.com/p/839748d571b2 链接2:https://www.jianshu.com/p/6d5f32aa5dda
- 【Python 多进程】
" 一.模块介绍 multiprocess模快 仔细说来,multiprocess不是一个模块,而是python中的一个操作.管理进程的包,之所以叫multi是取自multiple的多功能的 ...
- java泛型demo
1.泛型类 public class Dog<T> { private T age; public Dog(T age) { this.age = age; } public T getA ...
- firefox插件hostadmin自由切换host
在Mac下firefox插件hostadmin切换host,遇到的第一个问题就是提示权限不足,解决办法,在终端输入以下命令即可: sudo chmod og+w /etc/hosts chmod修改权 ...
- VBS微信轰炸
打开windows命令界面输入notepad将此vbs脚本复制粘贴到记事本,保存并设置后缀名为.vbs,进入微信或者QQ在聊天界面复制好要发送的文字,最后双击运行vbs脚本并把鼠标移入聊天框,最后按发 ...
- SSIS 从oracle 转数据回MS SQL
SSIS 从oracle 转数据回MS SQL,转每月的销售记录,大概15000以内,在开发机器上没问题,部署到生产环境出现各种状况“ SSIS 无法从连接伺服器的OLE DB提供者BULK 提取资料 ...
- Django框架之模板语言特殊标记(将后端的数据显示到前端)
后端代码 def GetMain(request): return render( request, "main.html", { "user1":" ...
- 电力电子实验 LLC半桥谐振变换器 记录
- Web Storage API:localStorage 和 SessionStorage
Web Storage API 提供了存储机制,通过该机制,浏览器可以安全地存储键值对,比使用 cookie 更加直观. 参考:https://developer.mozilla.org/zh-CN/ ...
- git 操作详情
Git 教程 1.Git 是一个开源的分布式版本控制系统 2.Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开源版本控制软件 3.Git 与常用的版本控制工具 ...