Python3学习之路~9.4 队列、生产者消费者模型

一 队列queue

当必须在多个线程之间安全地交换信息时,队列在线程编程中特别有用。

队列的作用:1.解耦,使程序直接实现松耦合 2.提高处理效率

列表与队列都是有顺序的,但是他们之间有一个很大的区别:从列表中取出一个数据,数据还在列表中,从队列中取出一个数据,队列中就减少一个数据。class queue.Queue(maxsize=0) #先入先出

class queue.LifoQueue(maxsize=0) #last in fisrt out 
class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列。
#maxsize是一个整数,用于设置可以放入队列的数据的上限。达到此大小后,插入将阻止,直到消耗队列项。
#如果maxsize小于或等于零,则队列大小为无限大。
#PriorityQueue首先检索priority_number最低值的数据(最低值是由sorted(list(entries))[0]返回)。
#放入PriorityQueue中的数据的典型模式是以下形式的元组:(priority_number,data)。

exception queue.Empty #在对空队列调用非阻塞的get()或get_nowait()时引发的异常。
exception queue.Full  #在对已满的队列调用非阻塞put()或put_nowait()时引发的异常。
Queue.qsize() #返回队列长度
Queue.empty() #如果队列为空,返回True 
Queue.full() #如果队列已满,返回True 

Queue.put(item, block=True, timeout=None) 
#将数据放入队列。如果block=True且timeout=None(默认值),则在必要时(队列满时进行put操作)阻塞,直到队列有空闲可用。
#若timeout=正数,它会阻塞最多超时秒,如果在该时间内队列没有空闲可用,则会引发Full异常。
#若block=False,则在必要时直接抛出Full异常(在这种情况下忽略超时)。

Queue.put_nowait(item) #相当于put(item, False)

Queue.get(block=True, timeout=None) 
#从队列中取出一个数据。如果block=True且timeout=None(默认值),则在必要时(队列空时进行get操作)阻塞,直到队列有数据可取。
#若timeout=正数,它会阻塞最多超时秒,如果在该时间内队列没有数据可取,则会引发Empty异常。
#若block=False,则在必要时直接抛出Empty异常(在这种情况下忽略超时)。

Queue.get_nowait() #相当于get(False)

#Two methods are offered to support tracking whether enqueued tasks have been fully processed by daemon consumer threads.

Queue.task_done()
#Indicate that a formerly enqueued task is complete. Used by queue consumer threads. 
#For each get() used to fetch a task, a subsequent call to task_done() tells the queue that the processing on the task is complete. #If a join() is currently blocking, it will resume when all items have been processed (meaning that a task_done() call
#was received for every item that had been put() into the queue). #Raises a ValueError if called more times than there were items placed in the queue. Queue.join() #block直到queue被消费完毕

 queue简单操作

C:UsersAdministrator>python3
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD6
4)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import queue
>>> q = queue.Queue() #实例化一个队列
>>> q.put("d1") #放入d1
>>> q.put("d2") #放入d2
>>> q.put("d3") #放入d3
>>> q.get() #不能指定取出哪个数据,必须按照先入先出规则
'd1'
>>> q.get()
'd2'
>>> q.get()
'd3'
>>> q.get() #当队列中没数据可取时,会卡住,无限等待

#如果不想卡住,可在get之前通过q.qsize()来判断队列长度,或者通过q.get_nowait()来抓住异常
>>> q.qsize()
0
>>> q.get_nowait()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:softwarePython3.6.5libqueue.py", line 192, in get_nowait
    return self.get(block=False)
  File "D:softwarePython3.6.5libqueue.py", line 161, in get
    raise Empty
queue.Empty

#Queue.get(block=True, timeout=None)方法默认有2个参数,可以手动改掉,使当队列为空时,不卡住。
>>> q.get(block=False) #当队列为空时,不卡住,直接报异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:softwarePython3.6.5libqueue.py", line 161, in get
    raise Empty
queue.Empty
>>> q.get(timeout=1)  #当队列为空时,卡住1秒,然后报异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:softwarePython3.6.5libqueue.py", line 172, in get
    raise Empty
queue.Empty

>>> import queue
>>> q = queue.Queue(maxsize=3) #可以设置队列长度最大为3
>>> q.put(1)
>>> q.put(2)
>>> q.put(3)
>>> q.put(4) #卡住,等待另一个线程把1取出来,4才能放进去

#Queue.put(item, block=True, timeout=None),法默认有2个参数,可以手动改掉,使当队列满时,不卡住。与get类似
>>> q.put(4,block=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:softwarePython3.6.5libqueue.py", line 130, in put
    raise Full
queue.Full
>>> q.put(4,timeout=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:softwarePython3.6.5libqueue.py", line 141, in put
    raise Full
queue.Full

>>> import queue
>>> q = queue.LifoQueue() #后进先出
>>> q.put(1)
>>> q.put(2)
>>> q.put(3)
>>> q.get()
3
>>> q.get()
2
>>> q.get()
1

>>> import queue
>>> q = queue.PriorityQueue() #优先级队列
>>> q.put((3,"zhao"))
>>> q.put((10,"qian"))
>>> q.put((6,"sun"))
>>> q.put((-1,"li"))
>>> q.get() #按数字由小到大
(-1, 'li')
>>> q.get()
(3, 'zhao')
>>> q.get()
(6, 'sun')
>>> q.get()
(10, 'qian')
queue实际操作

二 生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

下面来学习一个最基本的生产者消费者模型的例子

import threading,queue,time

q = queue.Queue(maxsize=10)

def Producer(name):
    count = 0
    while True:
        q.put("小鱼干%s" % count)
        print("[%s]生产了骨头"%name,count)
        count += 1
        time.sleep(0.5)
        
def Consumer(name):
    while True:
        print("[%s] 取到[%s]并且吃了它..." %(name,q.get()))
        time.sleep(1)

for i in range(2):
    p = threading.Thread(target=Producer, args=("主人%s"%i,))
    p.start()

for i in range(3):
    c = threading.Thread(target=Consumer,args=("大猫%s"%i,))
    c.start()

  

原文地址:https://www.cnblogs.com/zhengna/p/10554402.html