python--基础知识点--pyton中的五种队列

queue.Queue是进程内非阻塞队列,multiprocess.Queue是跨进程通信队列。

python中包含了五种队列,分别是queue.Queue() / asyncio.Queue() / multiprocessing.Queue() / multiprocessing.Manager().Queue()/collections.deque()

1.
1.1 collections.deque

deque是双端队列(double-ended queue)的缩写,由于两端都能编辑,deque既可以用来实现栈(stack)也可以用来实现队列(queue)。

deque支持丰富的操作方法,主要方法如图:
在这里插入图片描述
相比于list实现的队列,deque实现拥有更低的时间和空间复杂度。list实现在出队(pop)和插入(insert)时的空间复杂度大约为O(n),deque在出队(pop)和入队(append)时的时间复杂度是O(1)。

deque也支持in操作符,可以使用如下写法:

q = collections.deque([1, 2, 3, 4])
print(5 in q)  # False
print(1 in q)  # True

deque还封装了顺逆时针的旋转的方法:rotate。

# 顺时针
q = collections.deque([1, 2, 3, 4])
q.rotate(1)
print(q)  # [4, 1, 2, 3]
q.rotate(1)
print(q)  # [3, 4, 1, 2]

# 逆时针
q = collections.deque([1, 2, 3, 4])
q.rotate(-1)
print(q)  # [2, 3, 4, 1]
q.rotate(-1)
print(q)  # [3, 4, 1, 2]

线程安全方面,通过查看collections.deque中的append()、pop()等方法的源码可以知道,他们都是原子操作,所以是GIL保护下的线程安全方法。

static PyObject *
deque_append(dequeobject *deque, PyObject *item) { 
    Py_INCREF(item);
    if (deque_append_internal(deque, item, deque->maxlen) < 0) 
        return NULL;
    Py_RETURN_NONE;
}

通过dis方法可以看到,append是原子操作(一行字节码)。
在这里插入图片描述
综上,collections.deque是一个可以方便实现队列的数据结构,具有线程安全的特性,并且有很高的性能。

1.2 queue.Queue & asyncio.Queue

queue.Queue和asyncio.Queue都是支持多生产者、多消费者的队列,基于collections.deque,他们都提供了Queue(FIFO队列)、PriorityQueue(优先级队列)、LifoQueue(LIFO队列),接口方面也相同。

区别在于queue.Queue适用于多线程的场景(既可用于普通多线程间通信也可用于线程池中的多线程间通信),asyncio.Queue适用于协程场景下的通信,由于asyncio的加成,queue.Queue下的阻塞接口在asyncio.Queue中则是以返回协程对象的方式执行,具体差异如下表:

在这里插入图片描述

1.3 multiprocessing.Queue

multiprocessing提供了三种队列,分别是Queue、SimpleQueue、JoinableQueue。
在这里插入图片描述
multiprocessing.Queue既是线程安全也是进程安全的,相当于queue.Queue的多进程克隆版。multiprocessing.Queue支持put和get操作,底层结构是multiprocessing.Pipe。

multiprocessing.Queue底层是基于Pipe构建的,但是数据传递时并不是直接写入Pipe,而是写入进程本地buffer,通过一个feeder线程写入底层Pipe,这样做是为了实现超时控制和非阻塞put/get,所以Queue提供了join_thread、cancel_join_thread、close函数来控制feeder的行为,close函数用来关闭feeder线程、join_thread用来join feeder线程,cancel_join_thread用来在控制在进程退出时,不自动join feeder线程,使用cancel_join_thread有可能导致部分数据没有被feeder写入Pipe而导致的数据丢失。

multiprocessing.Queue默认不支持join()和task_done操作,这两个支持需要使用mp.JoinableQueue对象。

SimpleQueue是一个简化的队列,去掉了Queue中的buffer,没有了使用Queue可能出现的问题,但是put和get方法都是阻塞的并且没有超时控制。

1.4 multiprocessing.Manager().Queue()

multiprocessing.Manager().Queue()用于进程池,multiprocessing.Queue()可以用于多进程间的通信,但不能用于进程池中的多进程间通信。

1.5 总结

通过对比可以发现,上述四种结构都实现了队列,但是用处却各有偏重,collections.deque在数据结构层面实现了队列,但是并没有应用场景方面的支持,可以看做是一个基础的数据结构。queue模块实现了面向多生产线程、多消费线程的队列,asyncio.queue模块则实现了面向多生产协程、多消费协程的队列,而multiprocessing.queue模块实现了面向多成产进程、多消费进程的队列。

queue.Queue 是进程内非阻塞队列,实现进程内的多线程通信。
multiprocess.Queue 是跨进程通信队列
前者是各自进程私有, 后者是各子进程共有
Manager.Queue和 Queue,multiprocessing.Queue本质上没有太大关系,但它们有相同的方法

2. 五种队列所拥有的方法
2.1 collections.deque

常用方法:

  • deque.append(‘a’):在最右边添加一个元素,此时 d=deque(‘a’)
  • deque.appendleft(‘b’) :在最左边添加一个元素,此时 d=deque([‘b’, ‘a’])
  • deque.extend([‘c’,‘d’]) :在最右边添加所有元素,此时 d=deque([‘b’, ‘a’, ‘c’, ‘d’])
  • deque.extendleft([‘e’,‘f’]) :在最左边添加所有元素,此时 d=deque([‘f’, ‘e’, ‘b’, ‘a’, ‘c’, ‘d’])
  • deque.pop() :将最右边的元素取出,返回 ‘d’,此时 d=deque([‘f’, ‘e’, ‘b’, ‘a’, ‘c’])
  • deque.popleft():将最左边的元素取出,返回 ‘f’,此时 d=deque([‘e’, ‘b’, ‘a’, ‘c’])
  • deque.rotate(-2):向左旋转两个位置(正数则向右旋转),此时 d=deque([‘a’, ‘c’, ‘e’, ‘b’])
  • deque.count(‘a’):队列中’a’的个数,返回 1
  • deque.remove(‘c’):从队列中将’c’删除,此时 d=deque([‘a’, ‘e’, ‘b’])
  • deque.reverse():将队列倒序,此时 d=deque([‘b’, ‘e’, ‘a’])
2.2 queue.Queue

python提供了queue模块来专门实现消息队列。

queue模块有三种队列及构造函数

  • Python queue模块的FIFO队列先进先出。 queue.Queue(maxsize)

  • LIFO类似于堆,即先进后出。 queue.LifoQueue(maxsize)

  • 还有一种是优先级队列级别越低越先出来。 queue.PriorityQueue(maxsize)


queue只有gsize一个构造函数,用来指定队列容量,指定为0的时候代表容量无限。主要有以下成员函数:

  • Queue.gsize():返回消息队列的当前空间。返回的值不一定可靠。

  • Queue.empty():判断消息队列是否为空,返回True或者False。同样不可靠。

  • Queue.full():判断消息是否满。

  • Queue.put(item,block=True,timeout=None):往消息队列中存放数据。block可以控制是否阻塞,timeout控制阻塞时候的等待时间。如果不阻塞或者超时,会引起一个full exception。

  • Queue.put_nowait(item):相当于Queue.put(item,False)。

  • Queue.get(block=True,timeout=None):获取一个消息,block可以控制是否阻塞,timeout控制阻塞时候的等待时间。如果不阻塞或者超时,会引起一个full exception。

  • Queue.get_nowait(item) 相当于Queue.get(item,False)。

以下两个函数用来判断消息对应的任务是否完成:

Queue.task_done():接收消息的线程通过调用这个函来说明消息对应的任务已完成。

Queue.join():实际上意味着等到队列为空,再执行别的操作。

2.3 asyncio.Queue

asyncio模块有三种队列

  • Python queue模块的FIFO队列先进先出。
    asyncio.Queue(maxsize)
      maxsize表示队列中可存放的元素数量。如果 maxsize 小于等于零,则队列尺寸是无限的。如果是大于 0 的整数,则当队列达到 maxsize 时, awaitput() 将阻塞至某个元素被 get() 取出。
      不像标准库中的并发型 queue ,队列的尺寸一直是已知的,可以通过调用 qsize() 方法返回。
      这个类 不是线程安全的。

  • LIFO类似于堆,即先进后出。
    asyncio.LifoQueue(maxsize)
      Queue 的变体,先取出最近添加的条目(后进,先出)。

  • 还有一种是优先级队列级别越低越先出来。
    asyncio.PriorityQueue(maxsize)
      Queue 的变体;按优先级顺序取出条目 (最小的先取出)。条目通常是 (priority_number, data) 形式的元组。


asyncio队列

主要有以下成员函数:

  • qsize():返回队列用的元素数量。

  • empty():如果队列为空返回 True ,否则返回 False 。

  • full():如果有 maxsize 个条目在队列中,则返回 True 。如果队列用 maxsize=0 (默认)初始化,则 full() 永远不会返回 True 。

  • coroutine put(item):添加一个元素进队列。如果队列满了,在添加元素之前,会一直等待空闲插槽可用。

  • put_nowait(item):不阻塞的放一个元素入队列。如果没有立即可用的空闲槽,引发 QueueFull 异常。

  • coroutine get():从队列中删除并返回一个元素。如果队列为空,则等待,直到队列中有元素。

  • get_nowait():立即返回一个队列中的元素,如果队列内有值,否则引发异常 QueueEmpty 。


以下两个函数用来判断消息对应的任务是否完成:

  • coroutine join()
      阻塞至队列中所有的元素都被接收和处理完毕。
      当条目添加到队列的时候,未完成任务的计数就会增加。每当消费协程调用 task_done() 表示这个条目已经被回收,该条目所有工作已经完成,未完成计数就会减少。当未完成计数降到零的时候, join() 阻塞被解除。

  • task_done()
      表明前面排队的任务已经完成,即get出来的元素相关操作已经完成。
      由队列使用者控制。每个 get() 用于获取一个任务,任务最后调用 task_done() 告诉队列,这个任务已经完成。
      如果 join() 当前正在阻塞,在所有条目都被处理后,将解除阻塞(意味着每个 put() 进队列的条目的 task_done() 都被收到)。
      如果被调用的次数多于放入队列中的项目数量,将引发 ValueError 。

异常

exception asyncio.QueueEmpty:当队列为空的时候,调用 get_nowait() 方法而引发这个异常。

exception asyncio.QueueFull:当队列中条目数量已经达到它的 maxsize 的时候,调用 put_nowait() 方法而引发的异常。

2.4 multiprocessing.Queue与multiprocessing.JoinableQueue
2.4.1 multiprocessing.Queue

常用成员方法:

  • Queue.qsize():返回当前队列包含的消息数量;
  • Queue.empty():如果队列为空,返回True,反之False ;
  • Queue.full():如果队列满了,返回True,反之False;
  • Queue.put():将一个值添加进数列,可传参超时时长;
  • Queue.put_nowait():相当于Queue.get(False),当队列满了时报错:Full。
  • Queue.get():获取队列中的一条消息,然后将其从列队中移除,可传参超时时长;
  • Queue.get_nowait():相当Queue.get(False),取不到值时触发异常:Empty;
2.4.2 multiprocessing.JoinableQueue

multiprocessing.JoinableQueue继承于multiprocessing.Queue.

常用成员方法:

  • JoinableQueue.qsize():返回当前队列包含的消息数量;
  • JoinableQueue.empty():如果队列为空,返回True,反之False ;
  • JoinableQueue.full():如果队列满了,返回True,反之False;
  • JoinableQueue.put():将一个值添加进数列,可传参超时时长;
  • JoinableQueue.put_nowait():相当于Queue.get(False),当队列满了时报错:Full。
  • JoinableQueue.get():获取队列中的一条消息,然后将其从列队中移除,可传参超时时长;
  • JoinableQueue.get_nowait():相当Queue.get(False),取不到值时触发异常:Empty;
  • JoinableQueue.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
  • JoinableQueue.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
2.5 multiprocessing.Manager().Queue()

multiprocessing.Manager().Queue()对象的成员方法与queue.Queue()对象的成员方法类似

Manager支持的类型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。


[参考博客]https://www.jianshu.com/p/55243999aa56
     http://www.manongjc.com/article/68040.html
     https://blog.csdn.net/duxin_csdn/article/details/90517781

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值