參考:https://www.cnblogs.com/dbf-/p/11118628.html(https://www.cnblogs.com/dbf-/p/11118628.html
queue(隊列)
主要作用
1,解耦,使程式實作松耦合(一個子產品修改不會影響其他子產品)
2,提高效率
隊列于清單的差別
隊列中資料隻有一份,取出來就沒有了,差別于清單,清單資料取出來隻是複制了一份,隊列取出來相當于剪貼一份
分類
FIFO(先入先出)
預設即為先入先出
示例
queue_test.py
import queue
# 先入先出(預設)
q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
# 1
# 2
# 3
LIFO(先入後出)
定義方法 queue.LifoQueue()
# 先入後出
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
# 輸出
# 3
# 2
# 1
PriorityQueue(資料可設定優先級)
定義方法 queue.PriorityQueue()
同優先級按照ASCII排序
# 資料可設定優先級,同優先級按照ASCII排序
q = queue.PriorityQueue()
# 本次寫入元素為元組
q.put((2,'2'))
q.put((1,'1'))
q.put((3,'3'))
q.put((1,'a'))
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 輸出
# (1, '1')
# (1, 'a')
# (2, '2')
# (3, '3')
queue子產品
queue 子產品中有 Queue 類,LifoQueue、PriorityQueue 都繼承了 Queue
maxsize
maxsize 是執行個體化 Queue 類時的一個參數,預設為 0即隊列容量無限
Queue(maxsize=0) 可以控制隊列中資料的容量
假設把maxsize參數設定為3則當隊列已經有3個元素再put元素則會陷入阻塞狀态
# maxsize控制隊列容量,預設為0即預設無窮大
q = queue.Queue(maxsize=3)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
運作結果

put
Queue.put(block=True, timeout=None)
block用于設定是否阻塞,預設為True即阻塞,timeout用于設定阻塞等待時長即如果等待時長到了則退出阻塞抛出full錯誤
# put參數
# block=False設定不阻塞,當隊列滿了再插入資料則抛出queue.Full錯誤
q = queue.Queue(maxsize=3)
q.put(1, block=False)
q.put(2, block=False)
q.put(3, block=False)
q.put(4, block=False)
小結
阻塞
當隊列滿了之後,put 就會阻塞,一直等待隊列不再滿時向裡面添加資料
不阻塞
當隊列滿了之後,如果設定 put 不阻塞,或者等待時長到了之後會報錯:
queue.Full
get
Queue.get(block=True, timeout=None)
當隊列空了之後,get 就會阻塞,一直等待隊列中有資料後再擷取資料
當隊列空了之後,如果設定 get 不阻塞,或者等待時長到了之後會報錯:
_queue.Empty
# get參數
q = queue.Queue()
q.put(1)
print(q.get())
print(q.get())
get阻塞示例2
# 把block設定為False則隊列為空抛出queue.Empty錯誤
q = queue.Queue()
q.put(1)
print(q.get())
print(q.get(block=False))
full & empty
Queue.empty()
/
Queue.full()
用于判斷隊列是否為空、滿
盡量使用
qsize
代替
# full & empty
q = queue.Queue(maxsize=1)
q.put(1)
# 隊列滿了full()傳回True
print(q.full())
# True
# 隊列空了empty()傳回True
q.get()
print(q.empty())
# True
qszie
Queue.qsize()
用于擷取隊列中大緻的資料量
注意:在多線程的情況下不可靠
因為在擷取 qsize 時,其他線程可能又對隊列進行操作了
# qsize
q = queue.Queue()
q.put(1)
# 隊列元素數量為1
print(q.qsize())
# 1
join
join會在隊列存在未完成任務時阻塞,等待隊列無未完成任務,需要配合task_done使用
task_done
執行一次
put
會讓未完成任務 +1 ,但是執行
get
并不會讓未完成任務 -1 ,需要使用
task_done
讓未完成任務 -1 ,否則
join
就無法判斷
隊列為空時執行會報錯:
ValueError: task_done() called too many times
queue_test2.py
import queue
import threading
import time
def q_put():
for i in range(10):
q.put('1')
while True:
q.put('2')
time.sleep(1)
def q_get():
while True:
temp = q.get()
q.task_done()
print(temp)
time.sleep(0.3)
q = queue.Queue()
t1 = threading.Thread(target=q_put)
t2 = threading.Thread(target=q_get)
t1.start()
t2.start()
q.join()
print('queue is empty now')
運作輸出
輸出結果分析
1,t1線程運作q_put()函數首先往隊列q内放置10個1,然後沒隔1秒放入1個2
2,t2線程沒隔0.3秒從隊列q取資料,每次都運作task_done使隊列任務-1
3,運作到q.join()的時候,t1和t2線程還在持續運作,因為t2取資料的間隔時間比t1的間隔時間小,是以一定有取空隊列資料的時候
4,當取空隊列資料時,主線程運作列印‘queue is empty now’ 這個隊列為空是暫時的,即t1還在持續往隊列寫資料,t2持續讀取資料是以一直輸出2
注意:以下兩種情況會導緻無法列印‘queue is empty now‘
1,未加task_done語句,則未完成任務不會減少是以語言一直阻塞在join,輸出完1以後一直輸出2
2,q_get()睡眠時間比q_put()睡眠時間長,即往隊列寫資料比從隊列取資料速度快,未完成任務永遠不可能為0
生産者和消費者模型(主要用于解耦)
在多線程開發中,如果生産線程處理速度很快,而消費線程處理速度很慢,那麼生産線程就必須等到消費線程處理完,才能繼續生産資料。同樣的道理,如果消費線程的處理能力大于生産線程,那麼消費線程就必須等到生産線程。為了解決這個問題引入生産者和消費者模式。
生産者消費者模式是通過一個容器來解決生産者和消費者的強耦合問題。生産者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,是以生産者生産完資料之後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生産者要資料,而是直接從阻塞隊列裡取,阻塞隊列就相當于一個緩沖區,平衡了生産者和消費者的處理能力。
queue_test3.py
import threading
import time
import queue
def producer():
count = 1
while True:
q.put('No.%i' % count)
print('Producer put No.%i' % count)
time.sleep(1)
count += 1
def customer(name):
while True:
print('%s get %s' % (name, q.get()))
time.sleep(1.5)
q = queue.Queue(maxsize=5)
p = threading.Thread(target=producer, )
c = threading.Thread(target=customer, args=('jack', ))
p.start()
c.start()
運作輸出如下
解析