隊列的類別
threading.Queue
multiprocessing.Queue
queue.Queue
asyncio.Queue
List方式實現(xiàn)
隊列是一種只允許在一端進行插入操作彤灶,而在另一端進行刪除操作的線性表回溺。
在Python文檔中搜索隊列(queue)會發(fā)現(xiàn)艰赞,Python標(biāo)準(zhǔn)庫中包含了四種隊列,分別是queue.Queue / asyncio.Queue / multiprocessing.Queue / collections.deque描滔。
01
—
collections.deque
deque是雙端隊列(double-ended queue)的縮寫颗管,由于兩端都能編輯迄埃,deque既可以用來實現(xiàn)棧(stack)也可以用來實現(xiàn)隊列(queue)胎许。
deque支持豐富的操作方法,主要方法如圖:
相比于list實現(xiàn)的隊列栏妖,deque實現(xiàn)擁有更低的時間和空間復(fù)雜度乱豆。list實現(xiàn)在出隊(pop)和插入(insert)時的空間復(fù)雜度大約為O(n),deque在出隊(pop)和入隊(append)時的時間復(fù)雜度是O(1)吊趾。
deque也支持in操作符宛裕,可以使用如下寫法:
1q = collections.deque([1, 2, 3, 4])2print(5 in q) # False3print(1 in q) # True
deque還封裝了順逆時針的旋轉(zhuǎn)的方法:rotate。
1# 順時針 2q = collections.deque([1, 2, 3, 4]) 3q.rotate(1) 4print(q) # [4, 1, 2, 3] 5q.rotate(1) 6print(q) # [3, 4, 1, 2] 7 8# 逆時針 9q = collections.deque([1, 2, 3, 4])10q.rotate(-1)11print(q) # [2, 3, 4, 1]12q.rotate(-1)13print(q) # [3, 4, 1, 2]
線程安全方面论泛,通過查看collections.deque中的append()揩尸、pop()等方法的源碼可以知道,他們都是原子操作屁奏,所以是GIL保護下的線程安全方法岩榆。
1static PyObject *2deque_append(dequeobject *deque, PyObject *item) { 3 Py_INCREF(item);4 if (deque_append_internal(deque, item, deque->maxlen) < 0) 5 return NULL;6 Py_RETURN_NONE;7}
通過dis方法可以看到,append是原子操作(一行字節(jié)碼)。
綜上勇边,collections.deque是一個可以方便實現(xiàn)隊列的數(shù)據(jù)結(jié)構(gòu)犹撒,具有線程安全的特性,并且有很高的性能粒褒。
02
—
queue.Queue & asyncio.Queue
queue.Queue和asyncio.Queue都是支持多生產(chǎn)者识颊、多消費者的隊列,基于collections.deque奕坟,他們都提供了Queue(FIFO隊列)祥款、PriorityQueue(優(yōu)先級隊列)、LifoQueue(LIFO隊列)月杉,接口方面也相同刃跛。
區(qū)別在于queue.Queue適用于多線程的場景,asyncio.Queue適用于協(xié)程場景下的通信苛萎,由于asyncio的加成桨昙,queue.Queue下的阻塞接口在asyncio.Queue中則是以返回協(xié)程對象的方式執(zhí)行,具體差異如下表:
03
—
multiprocessing.Queue
multiprocessing提供了三種隊列首懈,分別是Queue绊率、SimpleQueue谨敛、JoinableQueue究履。
multiprocessing.Queue既是線程安全也是進程安全的,相當(dāng)于queue.Queue的多進程克隆版脸狸。和threading.Queue很像最仑,multiprocessing.Queue支持put和get操作,底層結(jié)構(gòu)是multiprocessing.Pipe炊甲。
multiprocessing.Queue底層是基于Pipe構(gòu)建的泥彤,但是數(shù)據(jù)傳遞時并不是直接寫入Pipe,而是寫入進程本地buffer卿啡,通過一個feeder線程寫入底層Pipe吟吝,這樣做是為了實現(xiàn)超時控制和非阻塞put/get,所以Queue提供了join_thread颈娜、cancel_join_thread剑逃、close函數(shù)來控制feeder的行為,close函數(shù)用來關(guān)閉feeder線程官辽、join_thread用來join feeder線程蛹磺,cancel_join_thread用來在控制在進程退出時,不自動join feeder線程同仆,使用cancel_join_thread有可能導(dǎo)致部分?jǐn)?shù)據(jù)沒有被feeder寫入Pipe而導(dǎo)致的數(shù)據(jù)丟失萤捆。
和threading.Queue不同的是,multiprocessing.Queue默認(rèn)不支持join()和task_done操作,這兩個支持需要使用mp.JoinableQueue對象俗或。
SimpleQueue是一個簡化的隊列市怎,去掉了Queue中的buffer,沒有了使用Queue可能出現(xiàn)的問題辛慰,但是put和get方法都是阻塞的并且沒有超時控制焰轻。
04
—
總結(jié)
通過對比可以發(fā)現(xiàn),上述四種結(jié)構(gòu)都實現(xiàn)了隊列昆雀,但是用處卻各有偏重辱志,collections.deque在數(shù)據(jù)結(jié)構(gòu)層面實現(xiàn)了隊列,但是并沒有應(yīng)用場景方面的支持,可以看做是一個基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)韭脊。queue模塊實現(xiàn)了面向多生產(chǎn)線程佳窑、多消費線程的隊列,asyncio.queue模塊則實現(xiàn)了面向多生產(chǎn)協(xié)程已球、多消費協(xié)程的隊列,而multiprocessing.queue模塊實現(xiàn)了面向多成產(chǎn)進程辅愿、多消費進程的隊列智亮。
05
—
參考
https://docs.python.org/3/library/collections.html#collections.deque
https://docs.python.org/3/library/queue.html
https://docs.python.org/3/library/asyncio-queue.html
https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue
https://bugs.python.org/issue15329
http://blog.ftofficer.com/2009/12/python-multiprocessing-3-about-queue/
http://cyrusin.github.io/2016/04/27/python-gil-implementaion/