Queue
Queue是python標准庫中的線程安全的隊列(FIFO)實現,提供了一個適用於多線程編程的先進先出的數據結構,即隊列,用來在生產者和消費者線程之間的信息傳遞
基本FIFO隊列
class Queue.Queue(maxsize=0)
FIFO即First in First Out,先進先出。Queue提供了一個基本的FIFO容器,使用方法很簡單,maxsize是個整數,指明了隊列中能存放的數據個數的上限。一旦達到上限,插入會導致阻塞,直到隊列中的數據被消費掉。如果maxsize小於或者等於0,隊列大小沒有限制。
舉個例子:
1 import Queue 2 3 q = Queue.Queue() 4 5 for i in range(5): 6 q.put(i) 7 8 while not q.empty(): 9 print q.get() 10 11 輸出結果: 12 0 13 1 14 2 15 3 16 4
LIFO隊列
class Queue.LifoQueue(maxsize=0)
LIFO即Last in First Out,后進先出。與棧的類似,使用也很簡單,maxsize用法同上
舉個例子:
1 import Queue 2 3 q = Queue.LifoQueue() 4 5 for i in range(5): 6 q.put(i) 7 8 while not q.empty(): 9 print q.get() 10 11 輸出: 12 13 4 14 3 15 2 16 1 17 0
可以看到僅僅是將Queue.Quenu類
替換為Queue.LifiQueue類
優先級隊列
class Queue.PriorityQueue(maxsize=0)
構造一個優先隊列。maxsize用法同上。
import Queue import threading class Job(object): def __init__(self, priority, description): self.priority = priority self.description = description print 'Job:',description return def __cmp__(self, other): return cmp(self.priority, other.priority) q = Queue.PriorityQueue() q.put(Job(3, 'level 3 job')) q.put(Job(10, 'level 10 job')) q.put(Job(1, 'level 1 job')) def process_job(q): while True: next_job = q.get() print 'for:', next_job.description q.task_done() workers = [threading.Thread(target=process_job, args=(q,)), threading.Thread(target=process_job, args=(q,)) ] for w in workers: w.setDaemon(True) w.start() q.join()
輸出結果:
Job: level 3 job Job: level 10 job Job: level 1 job for: level 1 job for: level 3 job for: job: level 10 job
一些常用方法
task_done()
意味着之前入隊的一個任務已經完成。由隊列的消費者線程調用。每一個get()調用得到一個任務,接下來的task_done()調用告訴隊列該任務已經處理完畢。
如果當前一個join()正在阻塞,它將在隊列中的所有任務都處理完時恢復執行(即每一個由put()調用入隊的任務都有一個對應的task_done()調用)。
join()
阻塞調用線程,直到隊列中的所有任務被處理掉。
只要有數據被加入隊列,未完成的任務數就會增加。當消費者線程調用task_done()(意味着有消費者取得任務並完成任務),未完成的任務數就會減少。當未完成的任務數降到0,join()解除阻塞。
put(item[, block[, timeout]])
將item放入隊列中。
- 如果可選的參數block為True且timeout為空對象(默認的情況,阻塞調用,無超時)。
- 如果timeout是個正整數,阻塞調用進程最多timeout秒,如果一直無空空間可用,拋出Full異常(帶超時的阻塞調用)。
- 如果block為False,如果有空閑空間可用將數據放入隊列,否則立即拋出Full異常
其非阻塞版本為put_nowait
等同於put(item, False)
get([block[, timeout]])
從隊列中移除並返回一個數據。block跟timeout參數同put
方法
其非阻塞方法為`get_nowait()`相當與get(False)
empty()
如果隊列為空,返回True,反之返回False
==================================================================================================
創建一個“隊列”對象
import Queue
myqueue = Queue.Queue(maxsize = 10)
Queue.Queue類即是一個隊列的同步實現。隊列長度可為無限或者有限。可通過Queue的構造函數的可選參數maxsize來設定隊列長度。如果maxsize小於1就表示隊列長度無限。
將一個值放入隊列中
myqueue.put(10)
調用隊列對象的put()方法在隊尾插入一個項目。put()有兩個參數,第一個item為必需的,為插入項目的值;第二個block為可選參數,默認為1。如果隊列當前為空且block為1,put()方法就使調用線程暫停,直到空出一個數據單元。如果block為0,put方法將引發Full異常。
PS:block參數為True,相當於隊伍排滿了,就在旁邊等着,騰出位置再插進去;block為False,相當於隊伍排滿了,沒位置了,直接投訴,不他媽跟你墨跡
將一個值從隊列中取出
myqueue.get()
調用隊列對象的get()方法從隊頭刪除並返回一個項目。可選參數為block,默認為True。如果隊列為空且block為True,get()就使調用線程暫停,直至有項目可用。如果隊列為空且block為False,隊列將引發Empty異常。
PS:block參數為True,相當於鍋里沒吃的了,就在旁邊等着上新菜,上了新菜再直接端走;block為False,相當於鍋里沒貨了,沒耐心等新菜上鍋,直接掀鍋砸灶,去你媽的
python queue模塊有三種隊列:
1、python queue模塊的FIFO隊列先進先出。
2、LIFO類似於堆。即先進后出。
3、還有一種是優先級隊列級別越低越先出來。
針對這三種隊列分別有三個構造函數:
1、class Queue.Queue(maxsize) FIFO
2、class Queue.LifoQueue(maxsize) LIFO
3、class Queue.PriorityQueue(maxsize) 優先級隊列
介紹一下此包中的常用方法:
Queue.qsize() 返回隊列的大小
Queue.empty() 如果隊列為空,返回True,反之False
Queue.full() 如果隊列滿了,返回True,反之False
Queue.full 與 maxsize 大小對應
Queue.get([block[, timeout]])獲取隊列,timeout等待時間
Queue.get_nowait() 相當Queue.get(False)
非阻塞 Queue.put(item) 寫入隊列,timeout等待時間
Queue.put_nowait(item) 相當Queue.put(item, False)
Queue.task_done() 在完成一項工作之后,Queue.task_done()函數向任務已經完成的隊列發送一個信號
Queue.join() 實際上意味着等到隊列為空,再執行別的操作
一些需要注意的地方:
1. 阻塞模式
import Queue
q = Queue.Queue(10)
......
for i in range(10):
q.put('A')
time.sleep(0.5)
這是一段極其簡單的代碼(另有兩個線程也在操作隊列q),我期望每隔0.5秒寫一個'A'到隊列中,但總是不能如願:間隔時間有時會遠遠超過0.5秒。原來,Queue.put()默認有 block = True 和 timeou 兩個參數。當 block = True 時,寫入是阻塞式的,阻塞時間由 timeou 確定。當隊列q被(其他線程)寫滿后,這段代碼就會阻塞,直至其他線程取走數據。Queue.put()方法加上 block=False 的參數,即可解決這個隱蔽的問題。但要注意,非阻塞方式寫隊列,當隊列滿時會拋出 exception Queue.Full 的異常。
PS:如果沒能及時put,說明隊伍滿了,還他媽在排隊呢,且等着
2. 無法捕獲 exception Queue.Empty 的異常
while True:
......
try:
data = q.get()
except Queue.Empty:
break
我的本意是用隊列為空時,退出循環,但實際運行起來,卻陷入了死循環。這個問題和上面有點類似:Queue.get()默認的也是阻塞方式讀取數據,隊列為空時,不會拋出 except Queue.Empty ,而是進入阻塞直至超時。 加上block=False 的參數,問題迎刃而解。
PS:老子還在排隊等上菜,你拋錘子的異常,有本事直接砸鍋掀灶台,否則乖乖一邊站着等