queue (隊列)
主要作用
- 解耦,使程序實現松耦合(一個模塊修改不會影響其他模塊)
- 提高效率
隊列與列表的關系
隊列中數據只有一份,取出就沒有了,區別於列表,列表數據取出只是復制了一份
分類
FIFO (先入先出)
queue.Queue(maxsize=0)
示例:
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
示例:
import queue
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 排序
示例:
import queue
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) 可以控制隊列中數據的容量
put
Queue.put(block=True, timeout=None)
block
用於設置是否阻塞, timeout
用於設置阻塞時等待時長
put_nowait() = put(block=False)
阻塞
當隊列滿了之后,put 就會阻塞,一直等待隊列不再滿時向里面添加數據
不阻塞
當隊列滿了之后,如果設置 put 不阻塞,或者等待時長到了之后會報錯:queue.Full
get
Queue.get(block=True, timeout=None)
get_nowait() = get(block=False)
阻塞
當隊列空了之后,get 就會阻塞,一直等待隊列中有數據后再獲取數據
不阻塞
當隊列空了之后,如果設置 get 不阻塞,或者等待時長到了之后會報錯:_queue.Empty
full & empty
Queue.empty()
/Queue.full()
用於判斷隊列是否為空、滿
盡量使用 qsize
代替
qsize
Queue.qsize()
用於獲取隊列中大致的數據量
注意:在多線程的情況下不可靠
因為在獲取 qsize 時,其他線程可能又對隊列進行操作了
join
join
會在隊列存在未完成任務時阻塞,等待隊列無未完成任務,需要配合 task_done
使用
task_done
執行一次 put
會讓未完成任務 +1 ,但是執行 get
並不會讓未完成任務 -1 ,需要使用 task_done
讓未完成任務 -1 ,否則 join
就無法判斷
隊列為空時執行會報錯:ValueError: task_done() called too many times
示例:
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')
主線程執行到 q.join
就開始阻塞,當 t2 線程將隊列中的數據全部取出之后,主線程才繼續執行。
如果將 task_done
注釋掉主線程就永遠阻塞在 q.join
,不再繼續向下執行
生產者消費者模型(主要用於解耦)
在多線程開發當中,如果生產線程處理速度很快,而消費線程處理速度很慢,那么生產線程就必須等待消費線程處理完,才能繼續生產數據。同樣的道理,如果消費線程的處理能力大於生產線程,那么消費線程就必須等待生產線程。為了解決這個問題於是引入了生產者和消費者模式
生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列里取,阻塞隊列就相當於一個緩沖區,平衡了生產者和消費者的處理能力。
示例:
import threading
import time
import queue
def producer():
count = 1
while 1:
q.put('No.%i' % count)
print('Producer put No.%i' % count)
time.sleep(1)
count += 1
def customer(name):
while 1:
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()
使用生成器也可以實現簡單的生產者消費者模型
點擊查看 -> 生成器