day 21 队列数据共享、消费者模型
昨日回顾
1.进程调度算法
-先来先服务
-短作业优先
-时间片轮转
-多级反馈队列
2.同步、异步、阻塞、非阻塞
-同步和异步:指的是回调方式,如果有回调就是异步,如果等待就是同步
-阻塞和非阻塞:指的是等待消息结果时的状态,如果在过程中,干了别的事,就是非阻塞,如果一直等,则是阻塞
3.进程Process类,实例化的时候得参数
-target:要执行的任务
-args:以位置参数传给任务传值
-kwargs:以关键字给任务传值
-name:进程名字(如果不写,会有个默认名字)
4.进程对象的属性和方法
-属性
-name
-pid:进程的id号,如果没有这个对象,需要借助os.getpid(),os.getppid()
-daemon:守护进程,如果设置为True,主进程挂了,该子进程也会挂。需要在p.start()之前执行
-方法
-is_alive() 进程是否存活
-terminate() 杀死该进程
-join() 主进程等待子进程结束后才继续执行-start() 子进程要执行必须调用
-run() 真正的任务
5.开启进程的另一种方式(类的继承)
写一个类,继承Process类
重写run方法,里面可以加入自己的任务
实例化得到写的类,调用这个类对象的start()方法
进程间数据是隔离的
6.进程锁
-多个进程操作同一个数据(文件中的数据,而不是内存中的数据)
-lock=Lock()
-加锁:lock.acquire()
-解锁:lock.release()
近日内容
1.进程Queue介绍
1.进程间数据隔离,两个进程进行通信,借助于Queue
2.进程间通信叫ipc:inter_Process Communication
-借助于Queue实现进程间通信
-借助于文件
-借助于数据库
-结束语消息队列:rabbitmq,kafka
基本使用
from multiprocessing import Process, Queue
if __name__ == '__main__':
# maxsize表示Queue的大小是多少,能放多少东西
queue = Queue(3) # 队列先进先出
# 放数据
queue.put("lem")
queue.put("lam")
queue.put("emt")
'''
# 实例化得到一个对象,数字表示queue的大小
queue=Queue(3)
# 放值
# block:是否阻塞
# timeout:等待时间
queue.put()
# 取值
queue.get()
不等待
queue.put_nowait()
queue.put_nowait()
#
queue.full()
queue.empty()
'''
2.通过Queue实现进程间通信
from multiprocessing import Process, Queue
import os
import time
def task(queue):
print(f"此进程{os.getpid()}开始放数据了")
time.sleep(10)
queue.put("lem is my wife")
print(f"此进程{os.getpid()}放完了")
if __name__ == '__main__':
# 不写数字,表示可以任意长度
queue = Queue()
p = Process(target=task, args=(queue,))
p.start()
res = queue.get() # 会卡在这
print(res)
3.批量生产数据放入Queue再批量取出
from multiprocessing import Process, Queue
import os
import time
def get_task(queue):
res = queue.get()
print(f"{os.getpid()}:这个进程去了这个数据{res}")
def put_task(queue):
queue.put(f"{os.getpid()}:放的数据")
# print(f"{os.getpid()}:放了数据")
if __name__ == '__main__':
queue = Queue(1)
p1 = Process(target=put_task, args=(queue,))
p2 = Process(target=put_task, args=(queue,))
p1.start()
p2.start()
p3 = Process(target=get_task, args=(queue,))
p4 = Process(target=get_task, args=(queue,))
p3.start()
p4.start()
4.生产者消费者模型(重点)
from multiprocessing import Process, Queue
import os
import random
import time
# def producer(queue):
# # 生产的东西放到queue中
# for i in range(10):
# msg = f"{os.getpid()}这个厨师蒸了第{i+1}个包子"
# print(msg)
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# queue.put(f"第{i+1}个包子")
#
#
# def consumer(queue):
# # 消费者从queue中取数据消费(吃包子)
# while True:
# res = queue.get()
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# print(f"{os.getpid()}这个消费者,吃了{res}")
#
#
#
# if __name__ == '__main__':
# queue = Queue()
#
# p1 = Process(target=producer, args=(queue,))
# p1.start()
#
# p2 = Process(target=consumer, args=(queue,))
# p2.start()
'===================================='
# # 改良(生产者已经)
#
# def producer(queue):
# # 生产的东西放到queue中
# for i in range(10):
# msg = f"{os.getpid()}这个厨师蒸了第{i + 1}个包子"
# print(msg)
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# queue.put(f"第{i + 1}个包子")
# queue.put(None)
#
#
# def consumer(queue):
# # 消费者从queue中取数据消费(吃包子)
# while True:
# res = queue.get()
# if not res: break # 如果取到None,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print(f"{os.getpid()}这个消费者,吃了{res}")
#
#
# if __name__ == '__main__':
# queue = Queue()
#
# p1 = Process(target=producer, args=(queue,))
# p1.start()
#
# p2 = Process(target=consumer, args=(queue,))
# p2.start()
# "===================================================="
# # 把put None放在主进程中执行
#
# def producer(queue):
# # 生产的东西放到queue中
# for i in range(10):
# msg = f"{os.getpid()}这个厨师蒸了第{i + 1}个包子"
# print(msg)
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# queue.put(f"第{i + 1}个包子")
#
#
#
# def consumer(queue):
# # 消费者从queue中取数据消费(吃包子)
# while True:
# res = queue.get()
# if not res: break # 如果取到None,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print(f"{os.getpid()}这个消费者,吃了{res}")
#
#
# if __name__ == '__main__':
# queue = Queue(3)
#
# p1 = Process(target=producer, args=(queue,))
# p1.start()
#
# p2 = Process(target=consumer, args=(queue,))
# p2.start()
#
# # 如果把put None放在这,会有问题
# # 主进程会先执行这句话,消费者进程读到None,直接结束,生产者进程没有结束,于是生产者一直生产,消费者不消费
# # 直到Queue满了就一直卡在这
# p1.join()
# queue.put(None)
5.多个生产者多个消费者的生产者消费者模型
"===================================================="
# 把put None放在主进程中执行
# def producer(queue, food):
# # 生产的东西放到queue中
# for i in range(10):
# msg = f"{os.getpid()}这个厨师做了第{i + 1}个{food}"
# print(msg)
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# queue.put(f"第{i + 1}个{food}")
#
#
# def consumer(queue):
# # 消费者从queue中取数据消费(吃包子)
# while True:
# res = queue.get()
# if not res: break # 如果取到None,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print(f"{os.getpid()}这个消费者,吃了{res}")
#
#
# if __name__ == '__main__':
# queue = Queue(3)
# # 起了三个生产者
# p1 = Process(target=producer, args=(queue, "包子"))
# p2 = Process(target=producer, args=(queue, "蛋糕"))
# p3 = Process(target=producer, args=(queue, "饼"))
# p1.start()
# p2.start()
# p3.start()
#
# # 起两个消费者
# c1 = Process(target=consumer, args=(queue,))
# c2 = Process(target=consumer, args=(queue,))
# c1.start()
# c2.start()
#
# # 等三个生产者都生产完,放三个None
# p1.join()
# p2.join()
# p3.join()
# queue.put(None)
# queue.put(None)
# queue.put(None)
# 如果消费者多,则多出来的会挂者
def producer(queue, food, name):
# 生产的东西放到queue中
for i in range(10):
msg = f"{name}这个厨师做了第{i + 1}个{food}"
print(msg)
# 模拟一下延迟
time.sleep(random.randint(1, 3))
queue.put(f"第{i + 1}个{food}")
def consumer(queue, name):
# 消费者从queue中取数据消费(吃包子)
while True:
try:
# 模拟一下延迟
time.sleep(random.randint(1, 3))
res = queue.get(timeout=20)
print(f"{name}这个消费者,吃了{res}")
except Exception as e:
print(e)
break
if __name__ == '__main__':
queue = Queue(3)
# 起了三个生产者
p1 = Process(target=producer, args=(queue, "包子", "rui"))
p2 = Process(target=producer, args=(queue, "蛋糕", "lem"))
p3 = Process(target=producer, args=(queue, "饼", "hina"))
p1.start()
p2.start()
p3.start()
# 起两个消费者
c1 = Process(target=consumer, args=(queue, "吃货1"))
c2 = Process(target=consumer, args=(queue, "吃货2"))
c1.start()
c2.start()
6.进程间数据共享(了解)
from multiprocessing import Process,Manager,Lock
# 魔法方法:类内以__开头__结尾的方法,都叫魔法方法,某种情况下会触发它的执行
'''
__init__ :类()触发
__new__:
__getattr__
__setattr__
__getitem__
__setitem__
'''
# def task(dic,lock):
# # lock.acquire()
# # dic['count']-=1
# # lock.release()
# with lock:
# dic['count'] -= 1
#
# if __name__ == '__main__':
# lock = Lock()
# with Manager() as m:
# # 如果直接定义dict,这个dict在多个进程中其实是多份,进程如果改,只改了自己的
# #如果定义的是m.dict({'count': 100}),多个进程之间就可以共享这个数据
# dic = m.dict({'count': 100})
#
# p_l = []
# for i in range(100):
# p = Process(target=task, args=(dic, lock))
# p_l.append(p)
# p.start()
# for p in p_l:
# p.join()
def task(dic,lock):
with lock:
dic['count'] -= 1
if __name__ == '__main__':
lock = Lock()
dic={'count':100}
p_l = []
for i in range(100):
p = Process(target=task, args=(dic, lock))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(dic)
7.线程概念
如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二
#进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。
from threading import Thread
from queue import Queue
import os
import time
def task():
time.sleep(3)
print('我是子线程执行的')
print(os.getpid())
if __name__ == '__main__':
# 启动线程
ctime = time.time()
t = Thread(target=task)
t.start()
# task()
time.sleep(3)
print(os.getpid())
print(time.time() - ctime)
作业
1.基于进程写生产者消费模型
def producer(queue, name, food):
for i in range(10):
print(f"{name}做了第{i + 1}个{food}")
time.sleep(random.randint(1, 3))
queue.put(f"第{i + 1}个包子")
def consumer(queue, name):
while True:
try:
res = queue.get(timeout=10)
time.sleep(random.randint(1, 3))
print(f"{name}吃了{res}")
except Exception as e:
print(e)
break
if __name__ == '__main__':
queue = Queue(3)
p1 = Process(target=producer, args=(queue, "lem", "包子"))
p2 = Process(target=producer, args=(queue, "hina", "蛋糕"))
p3 = Process(target=producer, args=(queue, "rui", "大饼"))
p1.start()
p2.start()
p3.start()
c1 = Process(target=consumer, args=(queue, "吃货1"))
c2 = Process(target=consumer, args=(queue, "吃货x"))
c1.start()
c2.start()
2.开启5个进程,执行1加到10000
5000050000
5000050000
5000050000
5000050000
5000050000
0.7479662895202637
3.开启5个线程,执行1加到100000
5000050000
5000050000
5000050000
5000050000
5000050000
0.0359036922454834
4.打印出上面两个的执行时间,比较进程快还是线程快(计算进程快,io线程快)