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