python的消息队列Queue的概念


 前言:

  我刚接触消息队列时,也是在网上百度搜索,发现好多博客写的有关消息队列Queue的时候,写的例子里都有多线程或者多进程的内容,把这些内容和Queue写在一起,我感觉如果是新手的话,是不太好理解的;因为从性能优化的角度来讲,消息队列也是为了分配任务,避免大量的并发请求过多过快的消耗服务器资源,从而优化产品的性能,这样的话,多线程或者多进程,又或者协程等等都是可以达到优化的目的,所以,我觉得在讲消息队列的内容时候,对于新手,还是不要涉及其他的内容比较好;

  在练习Queue的知识点时,我也有犯过些错误,比如说看了别人的博客,最后也实现了自己想要的效果,但是最后才反应过来,我写的代码,其实没有Queue也可以的,完全可以用Thread多线程来实现,而Queue的知识可以说是没有练习到,这也是我说上面一段话的背景;

  多线程和消息队列有什么相同和不同?

  相同点:都可以作为优化系统性能的方案;

  不同点:两者优化性能的方式不同,打个比方如下:

      1.比如用户访问登录接口,那么很多个用户同时访问该接口时,后台可以同一时刻为每一个用户单独起一个线程,让用户在最短时间内拿到登录结果,这也叫用户并发登录,这样对用户来说体验会好些,但是对系统来说,就很消耗资源,容易崩溃,这种场景用多线程比较合适(实际企业这样用的不多);

      2.再比如,测试人员经常要运行自动化案例,有时运行几个小时都结束不了,而且测试化境很复杂不好搭建,不能提拱并发执行,这时,可以考虑用消息队列了,每个人都可以在下班前提交一个测试任务,先提交的任务先运行,按顺序执行任务,等上一个测试任务执行完,再执行下一个的任务,像这种不要求立刻得到结果的场景,就可以考虑用消息队列了,说到这里,如果有需要了解学习自动化测试平台开发的小伙伴,欢迎和我交流,可以搜索并关注公众号“测试运维”,一起进步!

        3.最后,再打个比方来说明消息队列和多线程的区别,比如地铁站怎么处理客流高峰呢?一种方案是临时增加很多个刷卡通道并同时开放,使每个通道排队等候的人不多,这种方案可以让乘客很快进站,类似于多线程,这种方案对乘客来说是好事,减少了乘客的等待时间,但是对于地铁站来说就很难了,因为地铁站就那么大,不是想开几个通道就开几个,虽然技术上可以实现,但是不太现实;另一种方案,保持原有的刷卡通道个数不变,组织乘客排队刷卡进站,哪怕队已经排的很长很长,拐着弯排队,甚至是排到地铁站外面,只要大家都有序排队,最后都可以进站,这个方案就是类似消息队列,在队列管控上做改动,而不是在基础硬件设施上做改动,这种方案,对地铁站来说是好事,不用额外新建那么多通道,但是对乘客来说,等待的时间太长,体验不好; 

一.先源码实现一下queue,参考网上的。


class Queue(object): def __init__(self, max_item): self.queue = [] self.max_item = max_item def enqueue(self, item): for i in item: if len(self.queue) >= self.max_item: print('队列已满') return self.queue else: self.queue.append(i) return self.queue def dequeue(self): return self.queue.pop(0) def is_empty(self): return self.queue == [] def is_full(self): return len(self.queue) >= self.max_item def size(self): return len(self.queue) if __name__ == '__main__': queue = Queue(3) print(queue.enqueue([1,2,3,4])) #队列已满,[1, 2, 3] # print(queue.dequeue()) print(queue.is_full())

 

二,Python内置的四种队列的类型 (本次写的是LILO)

  1. LILO
    先进先出,只能在尾部插入元素,只能从头部取出元素
  2. LIFO
    先进后出,类似栈
  3. 优先队列
    队列元素为元组类型,即(优先级,数据)。
  4. 双端队列

三.常用的方法

  • q.task_done()    需要结合q.join来用,具体如下:

1.如果用了q.join(),那就一定要通过写q.task_done() ,来通知q.join当前事件已完成, 不写q.task_done()的话,q.join收不到当前事件完成时的信号,还会阻塞在q.join执行的地方等待接收信号,join后的代码得不到正常执行;

2. 同时不写q.join,和q.task_done() ,也是可以的,可以正常执行;
3.  只写q.task_done()不写q.join也是可以的,可以正常执行;

  • q.join()   直到queue队列中的所有事件都处理完毕,队列为空以后再执行q.join()之后的代码,如果用了q.join(),那就一定要写q.task_done()

  • q.put(item[, block[, timeout]])    向队列中增加事件,  .可以利用put和get进行传参,   #注意这里如果有多个参数,要用键值对或字典

  • q.get( block[, timeout]])   执行队列中的事件,执行完后将该事件从队列中删除,并返回当前的事件
  • q.qsize() 返回队列的大小

  • q.empty() 如果队列为空,返回True,反之False

  • q.full() 如果队列满了,返回True,反之False,Queue.full 与 maxsize 大小对应
  • q.get_nowait() 相当于Queue.get(False),非阻塞方法
  • q.queue   返回当前队列中所有的任务
 

 四. 一些需要注意的细节

  • 入队列时堵塞

 

from queue import Queue

#设置队列长度为1
que = Queue(maxsize=1)
#此时如果入队列两次, put将会一直堵塞,等待前一个出队列
que.put(1)
que.put(2)

 

  • 入队列非堵塞

 

from queue import Queue
que = Queue(maxsize=1)
#设置block为False为非堵塞,默认block为True堵塞

 try:

 
que.put(1)
#如果队列为满,非堵塞将会报异常
que.put(2,block=False)
except Exception:
pass
 

 

  • 出队列堵塞

 

from queue import Queue
que = Queue(maxsize=2)
que.put(1)
que.get()
que.get()
#如果队列为空,get会一直等待

 

  • 出队列非堵塞

 

from queue import Queue
que = Queue(maxsize=2)
que.put(1)
que.get()
try:
#如果设置为非堵塞,将会报异常
que.get(block=False)
except Exception:
pass

 

  • 设置超时

from queue import Queue

que = Queue(maxsize=1)
que.put(1)
try:
#设置超时后,如果2秒后还不能进队列将会报异常
#在非堵塞的情况下,无需设置超时
que.put(2,timeout=2)
except Exception:
pass
que.get()
try:
#设置超时后,如果2秒后队列仍是空将会报异常
#在非堵塞的情况下,无需设置超时
que.get(timeout=2)
except Exception:
pass

 

 

 
posted @ 2021-02-20 12:58  范若若  阅读(457)  评论(0编辑  收藏  举报