参考博客:https://www.cnblogs.com/itogo/p/5635629.html
一、简介
Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递。其作用可以使程序实现松耦合即解耦,另外就是提高效率。
二、基本队列
2.1 class queue.Queue(maxsize=0)
FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。
import queue # 先进先出 q = queue.Queue() for i in range(5): q.put(i) while not q.empty(): print(q.get())
#结果 0 1 2 3 4
2.2 queue.LifoQueue(maxsize=0)
LIFO即Last in First Out,后进先出。与栈的类似,使用也很简单,maxsize用法同上。
import queue # 后入先出 q = queue.LifoQueue() for i in range(5): q.put(i) while not q.empty(): print(q.get()) #输出 4 3 2 1 0
加入maxsize=3,如果在队列中存放的数据个数到3还没有取出来就会阻塞掉
import queue q = queue.LifoQueue(maxsize=3) for i in range(5): q.put(i) if q.qsize() > 2: print(q.get()) while not q.empty(): print(q.get()) # 输出 2 3 4 1 0
2.3 class queue.PriorityQueue(maxsize=0) 优先级队列
优先度越低的越先出来
import queue import threading q = queue.PriorityQueue() class JobThread(threading.Thread): def __init__(self, q_name): super(JobThread, self).__init__() self.q_name = q_name def run(self): while True: run_job = self.q_name.get() print(run_job[1]) if __name__ == '__main__': q.put((10, '10 level')) q.put((1, '1 level')) q.put((3, '3 level')) for i in range(2): t = JobThread(q) t.start()
结果:
1 level 3 level 10 level # 优先级越低的越先出来
三、常用方法
task_done() 意味着之前入队的一个任务已经完成。由队列的消费者线程调用。每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。 如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。 join() 阻塞调用线程,直到队列中的所有任务被处理掉。 只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。 put(item[, block[, timeout]]) 将item放入队列中。 如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。 如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。 如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常 其非阻塞版本为put_nowait等同于put(item, False) get([block[, timeout]]) 从队列中移除并返回一个数据。block跟timeout参数同put方法 其非阻塞方法为`get_nowait()`相当与get(False) empty() 如果队列为空,返回True,反之返回False
事例:
import queue import threading q = queue.PriorityQueue() class JobThread(threading.Thread): def __init__(self, q_name): super(JobThread, self).__init__() self.q_name = q_name def run(self): while True: run_job = self.q_name.get() print(run_job[1]) self.q_name.task_done() # task_done 使得阻塞后的队列可以加入 if __name__ == '__main__': q.put((10, '10 level')) q.put((1, '1 level')) q.put((3, '3 level')) for i in range(2): t = JobThread(q) t.start() q.join() # join 阻塞 q.put((5, '5 level'))
输出
1 level 3 level 10 level 5 level
四、生产者和消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
吃骨头的事例:
# -*- coding: UTF-8 -*- import threading import time import queue q = queue.Queue() lock = threading.Lock() count = 1 class ProducerThread(threading.Thread): def __init__(self, p_name): super(ProducerThread, self).__init__() self.p_name = p_name def run(self): global count # 骨头的数量累加 # count = 1 如果count 放在这的话,所有的生产者生产的数量从1开始 while True: lock.acquire() # 加锁,确保count数据安全 print('%s生成了骨头%s' % (self.p_name, count)) q.put("%s生成的骨头%s" % (self.p_name, count)) count += 1 time.sleep(1) lock.release() class ConsumerThread(threading.Thread): def __init__(self, c_name): super(ConsumerThread, self).__init__() self.c_name = c_name def run(self): while True: print("[%s] 取到了[%s],并且吃了它..." % (self.c_name, q.get())) # q.task_done() if __name__ == '__main__': for i in range(4): p = ProducerThread('Producer%s' % i) p.start() for i in range(5): c = ConsumerThread("dog%s" % i) c.start()