文章目錄
一、什麼是隊列?
Queue是python標準庫中的線程安全的隊列(FIFO)實現,提供了一個適用於多線程編程的先進先出的數據結構,即隊列,用來在生產者和消費者線程之間的信息傳遞。
隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。隊列中沒有元素時,稱爲空隊列。
二、線程爲什麼要使用Queue(隊列)?
1、在進程中,各個進程內存空間是獨立的, 彼此之間不直接通訊,只能引用了消息隊列來進行通訊。
2、一個線程中,多個線程內存空間是共享的,直接就能通信,引用Queue的作用也是通信,不多此一舉?
3、由於每個線程都能修改共享資源, 如果不使用互斥鎖,那麼得到的結果有可能不可預期,因爲你的一句代碼,彙編可能是多行,這樣在執行其中一行時,或許另一個線程開始對變量進行操作,這樣會導致結果出現偏差。
4、 而python自帶一個互斥鎖,和三個條件變量,來保證了線程安全。
三、線程使用Queue的好處
1、保障線程安全:隊列內置有鎖線程安全的數據結構,不用關心數據怎麼放的,只要知道怎麼用就可以,怎麼插數據拿數據
2、解耦: 使程序直接實現鬆耦合,修改一個函數,不會有串聯關係。
3、提高處理效率:FIFO = 現進先出,LIFO = 後入先出。
四、 Python四種類型的Queue
方法 | 功能 |
---|---|
Queue(maxsize=0) | 創建一個先進先出隊列,maxsize是個整數,指明瞭隊列中能存放的數據個數的上限。一旦達到上限,插入會導致阻塞,直到隊列中的數據被消費掉;否則爲無限隊列 |
LifoQueue(maxsize=0) | 創建一個後進先出的隊列,maxsize是個整數,指明瞭隊列中能存放的數據個數的上限。一旦達到上限,插入會導致阻塞,直到隊列中的數據被消費掉;否則爲無限隊列 |
Priority Queue(maxsize=0) | 創建一個優先級隊列,級別越低,越優先。 |
deque() | 雙邊隊列,也就是在序列的前後你都可以執行添加或刪除操作。 |
五、Queue對象的一些方法
方法 | 功能 |
---|---|
Queue.qsize() | 返回隊列大小 |
Queue.empty() | 如果隊列爲空,返回True,反之False |
Queue.full() | 如果隊列滿了,返回True,反之False |
Queue.put(item,block=True,timeout=None) | 將item放如隊列 |
Queue.put_nowait(item) | 和put(item, False)相同 |
Queue.get(block=True,timeout=None) | 從隊列中取得元素 |
Queue.get_nowait() | 和get(False)相同 |
Queue.task_done() | 在完成一項工作後,向任務已經完成的隊列發送一個信號 |
Queue.join() | 在隊列中所有元素執行完畢並調用上面的task_done()型號之前,保持阻塞 |
q.put方法用以插入數據到隊列中,put方法還有兩個可選參數:blocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,該方法會阻塞timeout指定的時間,直到該隊列有剩餘的空間。如果超時,會拋出Queue.Full異常。如果blocked爲False,但該Queue已滿,會立即拋出Queue.Full異常。
q.get方法可以從隊列讀取並且刪除一個元素。同樣,get方法有兩個可選參數:blocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,那麼在等待時間內沒有取到任何元素,會拋出Queue.Empty異常。如果blocked爲False,有兩種情況存在,如果Queue有一個值可用,則立即返回該值,否則,如果隊列爲空,則立即拋出Queue.Empty異常.
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
q.empty():調用此方法時q爲空則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中又加入了項目。
q.full():調用此方法時q已滿則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中的項目被取走。
q.qsize():返回隊列中目前項目的正確數量,結果也不可靠,理由同q.empty()和q.full()一樣
六、Queue隊列實例
6 .1 Queue實例
# 先進先出
# ==========================================
import queue
q=queue.Queue()
q.put('one')
q.put('two')
q.put('three')
print(q.get())
print(q.get())
print(q.get())
'''
結果(先進先出):
one
two
three
'''
6.2 LifoQueue 實例
# 先進後出概念
# ==========================================
import queue
q = queue.LifoQueue()
q.put('one')
q.put('two')
q.put('three')
print(q.get())
print(q.get())
print(q.get())
'''
結果(後進先出):
three
two
one
'''
6.3 PriorityQueue 實例
import queue
q = queue.PriorityQueue()
# put進入一個元組,元組的第一個元素是優先級(通常是數字,也可以是非數字之間的比較),數字越小優先級越高
q.put((20, 'a'))
q.put((10, 'b'))
q.put((30, 'c'))
print(q.get())
print(q.get())
print(q.get())
'''
結果(數字越小優先級越高,優先級高的優先出隊):
(10, 'b')
(20, 'a')
(30, 'c')
'''
6.4 collections. Deque 實例
# 雙邊隊列
# ===================================================================
import collections
dq = collections.deque(['a', 'b'])
dq.append('c') # 增加數據到隊尾
dq.appendleft('d') # 增加數據到隊首
print(dq) # 輸出隊列所有數據
print(dq.pop()) # 移除隊尾,並返回
print(dq.popleft()) # 移除隊首,並返回
'''
結果
deque(['d', 'a', 'b', 'c'])
c
d
'''
七、消費者和生產者模型
接下來的實例,是有關生產者-消費者,使用Queue對象,以及隨機生產(消費)的商品數量。生產者和消費者獨立且併發執行線程。
參考我其他博文:【python內功修煉003】:併發編程之生產者與消費者模式
from threading import Thread
import queue
import time, random
# 生產者
def producer(name,food,q):
for i in range(1, 5):
time.sleep(2) # 廚師每2秒做一個包子
print('廚師【%s】:製作了 %s 個%s' % (name, i, food))
res = '廚師【%s】製作的第【%s】%s' % (name, i, food)
q.put(res) # 將製作的包子裝在隊列裏面
# 消費者
def consumer(name, q):
while True:
res = q.get() # 獲取隊列中的值
time.sleep(random.randint(1, 2)) # 模擬吃包子時間
print('消費者【%s】:吃 %s\n' % (name, res))
q.task_done() # 發送信號
if __name__ == "__main__":
q = queue.Queue() # 創建一個隊列
# 生產者們:即廚師們
p1 = Thread(target=producer, args=('金鞍少年', '包子', q))
p2 = Thread(target=producer, args=('nancy', '牛肉麪', q))
# 消費者們:即吃貨們
c1 = Thread(target=consumer, args=('岳雲鵬', q,))
c2 = Thread(target=consumer, args=('郭德綱', q,))
# 設置守護進程
c1.daemon = True
c2.daemon = True
# 開始
p_l = [p1, p2, c1, c2]
for p in p_l:
p.start()
p1.join()
p2.join()
print('主')