隊列是一種只容許在一端進行插入操做,而在另外一端進行刪除操做的線性表。html
在Python文檔中搜索隊列(queue)會發現,Python標準庫中包含了四種隊列,分別是queue.Queue / asyncio.Queue / multiprocessing.Queue / collections.deque。python
deque是雙端隊列(double-ended queue)的縮寫,因爲兩端都能編輯,deque既能夠用來實現棧(stack)也能夠用來實現隊列(queue)。git
deque支持豐富的操做方法,主要方法如圖:github
相比於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保護下的線程安全方法。app
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是原子操做(一行字節碼)。async
綜上,collections.deque是一個能夠方便實現隊列的數據結構,具備線程安全的特性,而且有很高的性能。函數
queue.Queue和asyncio.Queue都是支持多生產者、多消費者的隊列,基於collections.deque,他們都提供了Queue(FIFO隊列)、PriorityQueue(優先級隊列)、LifoQueue(LIFO隊列),接口方面也相同。
區別在於queue.Queue適用於多線程的場景,asyncio.Queue適用於協程場景下的通訊,因爲asyncio的加成,queue.Queue下的阻塞接口在asyncio.Queue中則是以返回協程對象的方式執行,具體差別以下表:
queue.Queue | asyncio.Queue | |
---|---|---|
介紹 | 同步隊列 | asyncio隊列 |
線程安全 | 是 | 否 |
超時機制 | 經過timeout參數實現 | 經過asyncio.wait_for()方法實現 |
qsize() | 預估的隊列長度(獲取qsize到下一個操做之間,queue有可能被其它的線程修改,致使qsize大小發生變化) | 準確的隊列長度(因爲是單線程,因此queue不會被其它線程修改) |
put() / set() | put(item, block=True, timeout=None),能夠經過設置block是否爲True來配置put和set方法是否爲阻塞,而且能夠爲阻塞操做設置最大時長timeout,block爲False時行爲和put_nowait()方法一致。 | put()方法會返回一個協程對象,因此沒有block參數和timeout參數,若是須要非阻塞方法,可使用put_nowait(),若是須要對阻塞方法應用超時,可使用coroutine asyncio.wait_for()。 |
multiprocessing提供了三種隊列,分別是Queue、SimpleQueue、JoinableQueue。
multiprocessing.Queue既是線程安全也是進程安全的,至關於queue.Queue的多進程克隆版。和threading.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而致使的數據丟失。
和threading.Queue不一樣的是,multiprocessing.Queue默認不支持join()和task_done操做,這兩個支持須要使用mp.JoinableQueue對象。
SimpleQueue是一個簡化的隊列,去掉了Queue中的buffer,沒有了使用Queue可能出現的問題,可是put和get方法都是阻塞的而且沒有超時控制。
經過對比能夠發現,上述四種結構都實現了隊列,可是用處卻各有偏重,collections.deque在數據結構層面實現了隊列,可是並無應用場景方面的支持,能夠看作是一個基礎的數據結構。queue模塊實現了面向多生產線程、多消費線程的隊列,asyncio.queue模塊則實現了面向多生產協程、多消費協程的隊列,而multiprocessing.queue模塊實現了面向多成產進程、多消費進程的隊列。
blog.ftofficer.com/2009/12/pyt…
cyrusin.github.io/2016/04/27/…