Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
1、在进程中,各个进程内存空间是独立的, 彼此之间不直接通讯,只能引用了消息队列来进行通讯。
2、一个线程中,多个线程内存空间是共享的,直接就能通信,引用Queue的作用也是通信,不多此一举?
3、由于每个线程都能修改共享资源, 如果不使用互斥锁,那么得到的结果有可能不可预期,因为你的一句代码,汇编可能是多行,这样在执行其中一行时,或许另一个线程开始对变量进行操作,这样会导致结果出现偏差。
4、 而python自带一个互斥锁,和三个条件变量,来保证了线程安全。
1、保障线程安全:队列内置有锁线程安全的数据结构,不用关心数据怎么放的,只要知道怎么用就可以,怎么插数据拿数据
2、解耦: 使程序直接实现松耦合,修改一个函数,不会有串联关系。
3、提高处理效率:FIFO = 现进先出,LIFO = 后入先出。
方法 | 功能 |
---|---|
Queue(maxsize=0) | 创建一个先进先出队列,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉;否则为无限队列 |
LifoQueue(maxsize=0) | 创建一个后进先出的队列,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉;否则为无限队列 |
Priority Queue(maxsize=0) | 创建一个优先级队列,级别越低,越优先。 |
deque() | 双边队列,也就是在序列的前后你都可以执行添加或删除操作。 |
方法 | 功能 |
---|---|
Queue.qsize() | 返回队列大小 |
Queue.empty() | 如果队列为空,返回True,反之False |
Queue.full() | 如果队列满了,返回True,反之False |
Queue.put(item,block=True,timeout=None) | 将item放如队列 |
Queue.put_nowait(item) | 和put(item, False)相同 |
Queue.get(block=True,timeout=None) | 从队列中取得元素 |
Queue.get_nowait() | 和get(False)相同 |
Queue.task_done() | 在完成一项工作后,向任务已经完成的队列发送一个信号 |
Queue.join() | 在队列中所有元素执行完毕并调用上面的task_done()型号之前,保持阻塞 |
q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
# 先进先出
# ==========================================
import queue
q=queue.Queue()
q.put('one')
q.put('two')
q.put('three')
print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
one
two
three
'''
# 先进后出概念
# ==========================================
import queue
q = queue.LifoQueue()
q.put('one')
q.put('two')
q.put('three')
print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
three
two
one
'''
import queue
q = queue.PriorityQueue()
# put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20, 'a'))
q.put((10, 'b'))
q.put((30, 'c'))
print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
# 双边队列
# ===================================================================
import collections
dq = collections.deque(['a', 'b'])
dq.append('c') # 增加数据到队尾
dq.appendleft('d') # 增加数据到队首
print(dq) # 输出队列所有数据
print(dq.pop()) # 移除队尾,并返回
print(dq.popleft()) # 移除队首,并返回
'''
结果
deque(['d', 'a', 'b', 'c'])
c
d
'''
接下来的实例,是有关生产者-消费者,使用Queue对象,以及随机生产(消费)的商品数量。生产者和消费者独立且并发执行线程。
参考我其他博文:【python内功修炼003】:并发编程之生产者与消费者模式
from threading import Thread
import queue
import time, random
# 生产者
def producer(name,food,q):
for i in range(1, 5):
time.sleep(2) # 厨师每2秒做一个包子
print('厨师【%s】:制作了 %s 个%s' % (name, i, food))
res = '厨师【%s】制作的第【%s】%s' % (name, i, food)
q.put(res) # 将制作的包子装在队列里面
# 消费者
def consumer(name, q):
while True:
res = q.get() # 获取队列中的值
time.sleep(random.randint(1, 2)) # 模拟吃包子时间
print('消费者【%s】:吃 %s\n' % (name, res))
q.task_done() # 发送信号
if __name__ == "__main__":
q = queue.Queue() # 创建一个队列
# 生产者们:即厨师们
p1 = Thread(target=producer, args=('金鞍少年', '包子', q))
p2 = Thread(target=producer, args=('nancy', '牛肉面', q))
# 消费者们:即吃货们
c1 = Thread(target=consumer, args=('岳云鹏', q,))
c2 = Thread(target=consumer, args=('郭德纲', q,))
# 设置守护进程
c1.daemon = True
c2.daemon = True
# 开始
p_l = [p1, p2, c1, c2]
for p in p_l:
p.start()
p1.join()
p2.join()
print('主')