标题:Python生产者——消费者模型
本文章的生产者是可以选择多个的,同样消费者也是可以选择多个的,所以中间过程只能用文字叙述,不能用图片来演示(图片需要布局)
先说明,缓冲区最大限制定义了为10,如有个人需要,可以自定义一个entry框输入你的缓冲区容纳最大物品个数,某个状态下只能有一个进程(线程)进行
描述:生产者——消费者模型
在工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模式。结构图如下:
1.生产者模块
class Producer(threading.Thread): #该类自动继承threading.Thread,
def run(self): #所以会自动调用threading.Thread的__init__方法,只需要重写父类的run方法成你想要表达形式就行
global count #所以不需要写构造方法(__init__)
global key #count是用来记录物品个数的,key是用来互斥信号量,防止该进程执行会同时执行另一个进程两者都定义全局变量
while True:#循环
if count<10 and key: #如果物品未满,并且key为True(可访问)
key=False #进入后立即锁住,防止别的进程执行
t.insert("end","%s is producing 1 product\n" % (self.getName()))#将信息输入到文本框。getName是threading.Thread的方法,可获取进程名字
count+=1
t.insert("end","%d products in the queue\n" %(count))
t.update()
key=True #解锁
time.sleep(random.randrange(5)) #随机阻塞(0-5s)
消费者模块
class Consumer(threading.Thread): #和生产者一致
def run(self):
global count
global key
while True:
if count>0 and key:
key=False
t.insert("end","%s is consuming 1 product\n" % (self.getName()))
count-=1
t.insert("end","%d products in the queue\n" %(count))
t.update()
key=True
time.sleep(random.randrange(5))
main模块
def main():
o = eval(e.get()) #e为entry框,可输入一行,e.get()可以读取e输入的内容
e.delete('0','end') #读取后删除输入内容
q = eval(h.get())
h.delete('0','end')
for i in range(o): #o为e输入内容,为生产者个数
p = Producer() #初始化
p.start() #生产者进程执行,start方法是继承threading.Thread的,会自动调用run方法
for i in range(q): #q为h输入内容,为消费者个数
c = Consumer() #初始化
c.start() #消费者进程执行,start方法是继承threading.Thread的,会自动调用run方法
总的整合
from queue import Queue
from tkinter import *
import random,threading,time
count=0
key =True
mostkey=1
class Producer(threading.Thread):
def run(self):
global count
global key
global t
global mostkey
while True:
#global count
if count<10 and key:
key=False
t.insert("end","%s is producing 1 product\n" %(self.getName()))
count+=1
t.insert("end","%d products in the queue\n" %count)
t.update()
key=True
if mostkey==0:
break
#self.data.put(count)
time.sleep(random.randrange(5))
class Consumer(threading.Thread):
def run(self):
global count
global key
global t
global mostkey
while True:
#global count
if count>0 and key:
key=False
t.insert("end","%s is consuming 1 product\n" %(self.getName()))
count-=1
t.insert("end","%d products in the queue\n" %count)
t.update()
key=True
if mostkey==0:
break
time.sleep(random.randrange(5))
def main():
global e
global h
o = eval(e.get())
e.delete('0','end')
q = eval(h.get())
h.delete('0','end')
for i in range(o):
p = Producer()
p.start()#
for i in range(q):
c = Consumer()
c.start()
def SKT():#通过让key==True,让进程继续执行,达到类似于继续的效果
global key
key=True
def T1():#通过让key==False,让进程无法执行,达到类似于暂停的效果
global key
key=False
def DK():#设置mostkey互斥,可以跳出循环,让进程结束
global mostkey
mostkey=0
root =Tk()
root.title('生产者——消费者')
root.geometry("500x500")
lab1=Label(root,text="生产者个数",fg="red",bg="white").grid(row=0,column=1)
lab2=Label(root,text="消费者个数",fg="red",bg="white").grid(row=1,column=1)
global e
e = Entry(root, show=None)
global h
h = Entry(root, show=None)
e.grid(row=0,column=1,columnspan=2)
h.grid(row=1,column=1,columnspan=2)
global t
t = Text(root, height=100)
t.grid(row=2,column=2)
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=False)
filemenu.add_command(label='开始', command=main)
filemenu.add_command(label='暂停', command=T1)
filemenu.add_command(label='继续', command=SKT)
filemenu.add_command(label='结束', command=DK)
menubar.add_cascade(label='执行操作', menu=filemenu)
root.config(menu=menubar)
root.mainloop()#最好这里写窗口对象.mainloop(),如果套在另一个主窗口
#就可以对对总模块封装成一个def xxx():如果这里是mainloop。那么会一直报错主线程不在主循环里,百度了具体原因还未知,没有有用的信息
#毕竟代码能跑起来就行了,不要管为什么了
效果截图